From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mga18.intel.com (mga18.intel.com [134.134.136.126]) by mx.groups.io with SMTP id smtpd.web10.25470.1686851593481588403 for ; Thu, 15 Jun 2023 10:53:18 -0700 Authentication-Results: mx.groups.io; dkim=fail reason="unable to parse pub key" header.i=@intel.com header.s=intel header.b=OabVEcih; spf=pass (domain: intel.com, ip: 134.134.136.126, mailfrom: saloni.kasbekar@intel.com) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1686851598; x=1718387598; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=DGm2UNAqeZo32eywoabws+aAgBrtNuj/m1fsxGGuMKY=; b=OabVEcihepI5+J/8N82FzTigpRuDXw3ggopAujcFQG6+vYLkKiMZ/Itx 09DExFAJ+SoCdVaxgDwOFbXABMNYXzmHFwakSuYZb3Uhx3I66sIRpBFB7 2il65dKG0TFggvx2UU0f19mSob3embTF4+k1TcWA6WqQWJYQQGpKK59+q /98y2fa2Vvp9SOgWsKtnbgujBBdmHJgLxfHPdkvo+53rnYbz9weOjGpf+ HO/xz555dgaRiYaXbERLsqe0T1WFIm/QMT+se7R/u+7RvIXpygAaKztpa sxAFNI2EnCPoiN9HnXiyzm1heMn3zBWWZoc1rcorL8xGlKIkxmfz03Zed g==; X-IronPort-AV: E=McAfee;i="6600,9927,10742"; a="343732759" X-IronPort-AV: E=Sophos;i="6.00,245,1681196400"; d="scan'208";a="343732759" Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by orsmga106.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 15 Jun 2023 10:53:16 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6600,9927,10742"; a="857053653" X-IronPort-AV: E=Sophos;i="6.00,245,1681196400"; d="scan'208";a="857053653" Received: from fmbiosdev02.amr.corp.intel.com ([10.105.221.44]) by fmsmga001-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 15 Jun 2023 10:53:15 -0700 From: "Saloni Kasbekar" To: devel@edk2.groups.io Cc: Saloni Kasbekar , Sai Chaganty , Nate DeSimone , Isaac Oram , Rosen Chuang Subject: [PATCH 5/8] AlderlakeSiliconPkg/Pch: Add drivers Date: Thu, 15 Jun 2023 10:53:03 -0700 Message-Id: <73adf039d18a3d7b7e2c2eefaba9e4e0a9de782b.1686851565.git.saloni.kasbekar@intel.com> X-Mailer: git-send-email 2.36.1.windows.1 In-Reply-To: <6b58dd187980204b74c6ec718dad122674c213e1.1686851565.git.saloni.kasbekar@intel.com> References: <6b58dd187980204b74c6ec718dad122674c213e1.1686851565.git.saloni.kasbekar@intel.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Adds the following drivers: - PchSmiDispatcher - SmmControl Cc: Sai Chaganty Cc: Nate DeSimone Cc: Isaac Oram Cc: Rosen Chuang Signed-off-by: Saloni Kasbekar --- .../Pch/PchSmiDispatcher/Smm/PchSmiDispatch.c | 1857 +++++++++++++++++ .../PchSmiDispatcher/Smm/PchSmiDispatcher.inf | 106 + .../Pch/PchSmiDispatcher/Smm/PchSmiHelper.h | 34 + .../Pch/PchSmiDispatcher/Smm/PchSmm.h | 1028 +++++++++ .../Pch/PchSmiDispatcher/Smm/PchSmmCore.c | 905 ++++++++ .../Pch/PchSmiDispatcher/Smm/PchSmmGpi.c | 255 +++ .../Pch/PchSmiDispatcher/Smm/PchSmmHelpers.c | 332 +++ .../Pch/PchSmiDispatcher/Smm/PchSmmHelpers.h | 163 ++ .../Smm/PchSmmPeriodicTimer.c | 670 ++++++ .../PchSmiDispatcher/Smm/PchSmmPowerButton.c | 77 + .../Pch/PchSmiDispatcher/Smm/PchSmmSw.c | 381 ++++ .../Pch/PchSmiDispatcher/Smm/PchSmmSx.c | 117 ++ .../Pch/PchSmiDispatcher/Smm/PchSmmUsb.c | 244 +++ .../Pch/PchSmiDispatcher/Smm/PchxSmmHelpers.c | 682 ++++++ .../Pch/PchSmiDispatcher/Smm/PchxSmmHelpers.h | 107 + .../Pch/PchSmiDispatcher/Smm/PcieSmmClient.c | 38 + .../Pch/PchSmiDispatcher/Smm/SmmGlobalsPch.c | 20 + .../Pch/SmmControl/RuntimeDxe/SmmControl.inf | 53 + .../SmmControl/RuntimeDxe/SmmControlDriver.c | 338 +++ .../SmmControl/RuntimeDxe/SmmControlDriver.h | 122 ++ 20 files changed, 7529 insertions(+) create mode 100644 Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmiDispatch.c create mode 100644 Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmiDispatcher.inf create mode 100644 Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmiHelper.h create mode 100644 Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmm.h create mode 100644 Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmCore.c create mode 100644 Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmGpi.c create mode 100644 Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmHelpers.c create mode 100644 Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmHelpers.h create mode 100644 Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmPeriodicTimer.c create mode 100644 Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmPowerButton.c create mode 100644 Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmSw.c create mode 100644 Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmSx.c create mode 100644 Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmUsb.c create mode 100644 Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchxSmmHelpers.c create mode 100644 Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchxSmmHelpers.h create mode 100644 Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PcieSmmClient.c create mode 100644 Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/SmmGlobalsPch.c create mode 100644 Silicon/Intel/AlderlakeSiliconPkg/Pch/SmmControl/RuntimeDxe/SmmControl.inf create mode 100644 Silicon/Intel/AlderlakeSiliconPkg/Pch/SmmControl/RuntimeDxe/SmmControlDriver.c create mode 100644 Silicon/Intel/AlderlakeSiliconPkg/Pch/SmmControl/RuntimeDxe/SmmControlDriver.h diff --git a/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmiDispatch.c b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmiDispatch.c new file mode 100644 index 0000000000..be81459009 --- /dev/null +++ b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmiDispatch.c @@ -0,0 +1,1857 @@ +/** @file + This function handle the register/unregister of PCH specific SMI events. + + Copyright (c) 2022, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ +#include "PchSmmHelpers.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "PchSmiHelper.h" + + +/** + The internal function used to create and insert a database record + for SMI record of Pch Smi types. + + @param[in] SrcDesc The pointer to the SMI source description + @param[in] DispatchFunction Pointer to dispatch function to be invoked for this SMI source + @param[in] PchSmiType Specific SMI type of PCH SMI + @param[out] DispatchHandle Handle of dispatch function to register. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source description + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database record + @retval EFI_SUCCESS The database record is created successfully. +**/ +EFI_STATUS +PchSmiRecordInsert ( + IN CONST PCH_SMM_SOURCE_DESC *SrcDesc, + IN PCH_SMI_CALLBACK_FUNCTIONS DispatchFunction, + IN PCH_SMI_TYPES PchSmiType, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + DATABASE_RECORD Record; + + if (SrcDesc == NULL) { + return EFI_INVALID_PARAMETER; + } + + ZeroMem (&Record, sizeof (DATABASE_RECORD)); + // + // Gather information about the registration request + // + Record.Signature = DATABASE_RECORD_SIGNATURE; + Record.PchSmiCallback = DispatchFunction; + Record.ProtocolType = PchSmiDispatchType; + Record.PchSmiType = PchSmiType; + + CopyMem (&Record.SrcDesc, SrcDesc, sizeof (PCH_SMM_SOURCE_DESC)); + Status = SmmCoreInsertRecord ( + &Record, + DispatchHandle + ); + ASSERT_EFI_ERROR (Status); + + return EFI_SUCCESS; +} + + +// +// TCO_STS bit that needs to be cleared +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mDescSrcTcoSts = { + PCH_SMM_NO_FLAGS, + { + NULL_BIT_DESC_INITIALIZER, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_TCO + } + }, + NULL_BIT_DESC_INITIALIZER +}; + +/** + Clear the TCO SMI status bit and block after the SMI handling is done + + @param[in] SrcDesc Pointer to the PCH SMI source description table + +**/ +VOID +EFIAPI +PchTcoSmiClearSourceAndBlock ( + CONST PCH_SMM_SOURCE_DESC *SrcDesc + ) +{ + PchSmmClearSourceAndBlock (SrcDesc); + // + // Any TCO-based status bits require special handling. + // SMI_STS.TCO_STS must be cleared in addition to the status bit in the TCO registers + // + PchSmmClearSource (&mDescSrcTcoSts); +} + +/** + Clear the TCO SMI status bit after the SMI handling is done + + @param[in] SrcDesc Pointer to the PCH SMI source description table + +**/ +VOID +EFIAPI +PchTcoSmiClearSource ( + CONST PCH_SMM_SOURCE_DESC *SrcDesc + ) +{ + PchSmmClearSource (SrcDesc); + // + // Any TCO-based status bits require special handling. + // SMI_STS.TCO_STS must be cleared in addition to the status bit in the TCO registers + // + PchSmmClearSource (&mDescSrcTcoSts); +} + +/** + Initialize Source descriptor structure + + @param[in] SrcDesc Pointer to the PCH SMI source description table + +**/ +VOID +EFIAPI +NullInitSourceDesc ( + PCH_SMM_SOURCE_DESC *SrcDesc + ) +{ + ZeroMem (SrcDesc, sizeof (PCH_SMM_SOURCE_DESC)); + SrcDesc->En[0].Reg.Type = PCH_SMM_ADDR_TYPE_NULL; + SrcDesc->En[1].Reg.Type = PCH_SMM_ADDR_TYPE_NULL; + SrcDesc->Sts[0].Reg.Type = PCH_SMM_ADDR_TYPE_NULL; + SrcDesc->PmcSmiSts.Reg.Type = PCH_SMM_ADDR_TYPE_NULL; +} + +// +// Mch srcdesc +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSrcDescMch = { + PCH_SMM_NO_FLAGS, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_EN} + }, + S_ACPI_IO_SMI_EN, + N_ACPI_IO_SMI_EN_TCO + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + TCO_ADDR_TYPE, + {R_TCO_IO_TCO1_STS} + }, + S_TCO_IO_TCO1_STS, + N_TCO_IO_TCO1_STS_DMISMI + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_TCO + } +}; + +/** + The register function used to register SMI handler of MCH event. + + @param[in] This The pointer to the protocol itself + @param[in] DispatchFunction Pointer to dispatch function to be invoked for this SMI source + @param[out] DispatchHandle Handle of dispatch function to register. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source description + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database record + @retval EFI_SUCCESS The database record is created successfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock event has been triggered +**/ +EFI_STATUS +EFIAPI +PchTcoSmiMchRegister ( + IN PCH_TCO_SMI_DISPATCH_PROTOCOL *This, + IN PCH_TCO_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + DATABASE_RECORD *Record; + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock == TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock event has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status = PchSmiRecordInsert ( + &mSrcDescMch, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchTcoSmiMchType, + DispatchHandle + ); + if (!EFI_ERROR (Status)) { + Record = DATABASE_RECORD_FROM_LINK (*DispatchHandle); + Record->ClearSource = PchTcoSmiClearSource; + PchSmmClearSource (&Record->SrcDesc); + PchSmmEnableSource (&Record->SrcDesc); + SmiHandlerProfileRegisterHandler (&gPchTcoSmiDispatchProtocolGuid, (EFI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NULL, 0); + } + return Status; +} + +// +// TcoTimeout srcdesc +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSrcDescTcoTimeout = { + PCH_SMM_NO_FLAGS, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_EN} + }, + S_ACPI_IO_SMI_EN, + N_ACPI_IO_SMI_EN_TCO + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + TCO_ADDR_TYPE, + {R_TCO_IO_TCO1_STS} + }, + S_TCO_IO_TCO1_STS, + N_TCO_IO_TCO1_STS_TIMEOUT + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_TCO + } +}; + +/** + The register function used to register SMI handler of TcoTimeout event. + + @param[in] This The pointer to the protocol itself + @param[in] DispatchFunction Pointer to dispatch function to be invoked for this SMI source + @param[out] DispatchHandle Handle of dispatch function to register. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source description + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database record + @retval EFI_SUCCESS The database record is created successfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock event has been triggered +**/ +EFI_STATUS +EFIAPI +PchTcoSmiTcoTimeoutRegister ( + IN PCH_TCO_SMI_DISPATCH_PROTOCOL *This, + IN PCH_TCO_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + DATABASE_RECORD *Record; + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock == TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock event has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status = PchSmiRecordInsert ( + &mSrcDescTcoTimeout, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchTcoSmiTcoTimeoutType, + DispatchHandle + ); + if (!EFI_ERROR (Status)) { + Record = DATABASE_RECORD_FROM_LINK (*DispatchHandle); + Record->ClearSource = PchTcoSmiClearSource; + PchSmmClearSource (&Record->SrcDesc); + PchSmmEnableSource (&Record->SrcDesc); + SmiHandlerProfileRegisterHandler (&gPchTcoSmiDispatchProtocolGuid, (EFI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NULL, 0); + } + return Status; +} + +// +// OsTco srcdesc +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSrcDescOsTco = { + PCH_SMM_NO_FLAGS, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_EN} + }, + S_ACPI_IO_SMI_EN, + N_ACPI_IO_SMI_EN_TCO + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + TCO_ADDR_TYPE, + {R_TCO_IO_TCO1_STS} + }, + S_TCO_IO_TCO1_STS, + N_TCO_IO_TCO1_STS_SW_TCO_SMI + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_TCO + } +}; + +/** + The register function used to register SMI handler of OS TCO event. + + @param[in] This The pointer to the protocol itself + @param[in] DispatchFunction Pointer to dispatch function to be invoked for this SMI source + @param[out] DispatchHandle Handle of dispatch function to register. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source description + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database record + @retval EFI_SUCCESS The database record is created successfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock event has been triggered +**/ +EFI_STATUS +EFIAPI +PchTcoSmiOsTcoRegister ( + IN PCH_TCO_SMI_DISPATCH_PROTOCOL *This, + IN PCH_TCO_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + DATABASE_RECORD *Record; + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock == TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock event has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status = PchSmiRecordInsert ( + &mSrcDescOsTco, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchTcoSmiOsTcoType, + DispatchHandle + ); + if (!EFI_ERROR (Status)) { + Record = DATABASE_RECORD_FROM_LINK (*DispatchHandle); + Record->ClearSource = PchTcoSmiClearSource; + PchSmmClearSource (&Record->SrcDesc); + PchSmmEnableSource (&Record->SrcDesc); + SmiHandlerProfileRegisterHandler (&gPchTcoSmiDispatchProtocolGuid, (EFI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NULL, 0); + } + return Status; +} + +// +// Nmi +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSrcDescNmi = { + PCH_SMM_NO_FLAGS, + { + { + { + TCO_ADDR_TYPE, + {R_TCO_IO_TCO1_CNT} + }, + S_TCO_IO_TCO1_CNT, + N_TCO_IO_TCO1_CNT_NMI2SMI_EN + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + TCO_ADDR_TYPE, + {R_TCO_IO_TCO1_STS} + }, + S_TCO_IO_TCO1_STS, + N_TCO_IO_TCO1_STS_NMI2SMI + } + }, + // + // NOTE: The status of NMI2SMI won't reflect to PMC SMI_STS. + // So skip the top level status check and check the TCO1_STS directly. + // + NULL_BIT_DESC_INITIALIZER +}; + +/** + The register function used to register SMI handler of NMI event. + + @param[in] This The pointer to the protocol itself + @param[in] DispatchFunction Pointer to dispatch function to be invoked for this SMI source + @param[out] DispatchHandle Handle of dispatch function to register. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source description + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database record + @retval EFI_SUCCESS The database record is created successfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock event has been triggered +**/ +EFI_STATUS +EFIAPI +PchTcoSmiNmiRegister ( + IN PCH_TCO_SMI_DISPATCH_PROTOCOL *This, + IN PCH_TCO_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + DATABASE_RECORD *Record; + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock == TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock event has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status = PchSmiRecordInsert ( + &mSrcDescNmi, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchTcoSmiNmiType, + DispatchHandle + ); + if (!EFI_ERROR (Status)) { + Record = DATABASE_RECORD_FROM_LINK (*DispatchHandle); + Record->ClearSource = PchTcoSmiClearSource; + PchSmmClearSource (&Record->SrcDesc); + PchSmmEnableSource (&Record->SrcDesc); + SmiHandlerProfileRegisterHandler (&gPchTcoSmiDispatchProtocolGuid, (EFI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NULL, 0); + } + return Status; +} + +// +// IntruderDetect srcdesc +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSrcDescIntruderDet = { + PCH_SMM_NO_FLAGS, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_EN} + }, + S_ACPI_IO_SMI_EN, + N_ACPI_IO_SMI_EN_TCO + }, + { + { + TCO_ADDR_TYPE, + {R_TCO_IO_TCO2_CNT} + }, + S_TCO_IO_TCO2_CNT, + N_TCO_IO_TCO2_CNT_INTRD_SEL + } + }, + { + { + { + TCO_ADDR_TYPE, + {R_TCO_IO_TCO2_STS} + }, + S_TCO_IO_TCO2_STS, + N_TCO_IO_TCO2_STS_INTRD_DET + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_TCO + } +}; + +/** + The register function used to register SMI handler of Intruder Detect event. + + @param[in] This The pointer to the protocol itself + @param[in] DispatchFunction Pointer to dispatch function to be invoked for this SMI source + @param[out] DispatchHandle Handle of dispatch function to register. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source description + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database record + @retval EFI_SUCCESS The database record is created successfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock event has been triggered +**/ +EFI_STATUS +EFIAPI +PchTcoSmiIntruderDetRegister ( + IN PCH_TCO_SMI_DISPATCH_PROTOCOL *This, + IN PCH_TCO_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + DATABASE_RECORD *Record; + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock == TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock event has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status = PchSmiRecordInsert ( + &mSrcDescIntruderDet, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchTcoSmiIntruderDetectType, + DispatchHandle + ); + if (!EFI_ERROR (Status)) { + Record = DATABASE_RECORD_FROM_LINK (*DispatchHandle); + Record->ClearSource = PchTcoSmiClearSourceAndBlock; + PchSmmClearSource (&Record->SrcDesc); + PchSmmEnableSource (&Record->SrcDesc); + SmiHandlerProfileRegisterHandler (&gPchTcoSmiDispatchProtocolGuid, (EFI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NULL, 0); + } + return Status; +} + +// +// SpiBiosWp srcdesc +// +GLOBAL_REMOVE_IF_UNREFERENCED PCH_SMM_SOURCE_DESC mSrcDescSpiBiosWp = { + PCH_SMM_NO_FLAGS, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_EN} + }, + S_ACPI_IO_SMI_EN, + N_ACPI_IO_SMI_EN_TCO + }, + { + { + PCIE_ADDR_TYPE, + { 0xFFFFFFFF } // to be updated in PchSmiDispatchUpdateDescriptors + }, + S_SPI_CFG_BC, + N_SPI_CFG_BC_BLE + }, + }, + { + { + { + PCIE_ADDR_TYPE, + { 0xFFFFFFFF } // to be updated in PchSmiDispatchUpdateDescriptors + }, + S_SPI_CFG_BC, + N_SPI_CFG_BC_SYNC_SS + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_TCO + } +}; + +/** + Special handling for SPI Write Protect + + @param[in] SrcDesc Not used +**/ +VOID +EFIAPI +PchTcoSpiWpClearSource ( + CONST PCH_SMM_SOURCE_DESC *SrcDesc + ) +{ + UINT64 SpiRegBase; + UINT32 BiosControl; + UINT32 Timeout; + + SpiRegBase = SpiPciCfgBase (); + PciSegmentAndThenOr32 ( + SpiRegBase + R_SPI_CFG_BC, + (UINT32) ~B_SPI_CFG_BC_ASYNC_SS, + B_SPI_CFG_BC_SYNC_SS + ); + // + // Ensure the SYNC is cleared + // + Timeout = 1000; + do { + BiosControl = PciSegmentRead32 (SpiRegBase + R_SPI_CFG_BC); + Timeout--; + } while ((BiosControl & B_SPI_CFG_BC_SYNC_SS) && (Timeout > 0)); + + // + // Any TCO-based status bits require special handling. + // SMI_STS.TCO_STS must be cleared in addition to the status bit in the TCO registers + // + PchSmmClearSource (&mDescSrcTcoSts); +} + +/** + Set SMI_EN_TCO to enable TCO SMI. +**/ +STATIC +VOID +PchSetSmiEnTco ( + VOID + ) +{ + IoOr32 (mAcpiBaseAddr + R_ACPI_IO_SMI_EN, B_ACPI_IO_SMI_EN_TCO); +} + +/** + The register function used to register SMI handler of BIOS write protect event. + + @param[in] This The pointer to the protocol itself + @param[in] DispatchFunction Pointer to dispatch function to be invoked for this SMI source + @param[out] DispatchHandle Handle of dispatch function to register. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source description + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database record + @retval EFI_SUCCESS The database record is created successfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock event has been triggered +**/ +EFI_STATUS +EFIAPI +PchTcoSmiSpiBiosWpRegister ( + IN PCH_TCO_SMI_DISPATCH_PROTOCOL *This, + IN PCH_TCO_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + DATABASE_RECORD *Record; + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock == TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock event has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status = PchSmiRecordInsert ( + &mSrcDescSpiBiosWp, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchTcoSmiSpiBiosWpType, + DispatchHandle + ); + if (!EFI_ERROR (Status)) { + Record = DATABASE_RECORD_FROM_LINK (*DispatchHandle); + Record->ClearSource = PchTcoSpiWpClearSource; + PchTcoSpiWpClearSource (NULL); + // + // It doesn't enable the BIOSLOCK here. Enable it by policy in DXE. + // Only enable SMI_EN_TCO. + // + PchSetSmiEnTco (); + SmiHandlerProfileRegisterHandler (&gPchTcoSmiDispatchProtocolGuid, (EFI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NULL, 0); + } + return Status; +} + +// +// LpcBiosWp srcdesc +// +GLOBAL_REMOVE_IF_UNREFERENCED PCH_SMM_SOURCE_DESC mSrcDescLpcBiosWp = { + PCH_SMM_NO_FLAGS, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_EN} + }, + S_ACPI_IO_SMI_EN, + N_ACPI_IO_SMI_EN_TCO + }, + { + { + PCIE_ADDR_TYPE, + { 0xFFFFFFFF } // to be updated in PchSmiDispatchUpdateDescriptors + }, + S_LPC_CFG_BC, + N_LPC_CFG_BC_LE + } + }, + { + { + { + TCO_ADDR_TYPE, + {R_TCO_IO_TCO1_STS} + }, + S_TCO_IO_TCO1_STS, + N_TCO_IO_TCO1_STS_BIOSWR + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_TCO + } +}; + +/** + The register function used to register SMI handler of LPC BIOS write protect event. + + @param[in] This The pointer to the protocol itself + @param[in] DispatchFunction Pointer to dispatch function to be invoked for this SMI source + @param[out] DispatchHandle Handle of dispatch function to register. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source description + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database record + @retval EFI_SUCCESS The database record is created successfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock event has been triggered +**/ +EFI_STATUS +EFIAPI +PchTcoSmiLpcBiosWpRegister ( + IN PCH_TCO_SMI_DISPATCH_PROTOCOL *This, + IN PCH_TCO_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + DATABASE_RECORD *Record; + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock == TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock event has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + if (IsEspiEnabled ()) { + // + // Status is D31F0's PCBC.BWPDS + // + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } + + Status = PchSmiRecordInsert ( + &mSrcDescLpcBiosWp, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchTcoSmiLpcBiosWpType, + DispatchHandle + ); + if (!EFI_ERROR (Status)) { + Record = DATABASE_RECORD_FROM_LINK (*DispatchHandle); + Record->ClearSource = PchTcoSmiClearSource; + PchSmmClearSource (&Record->SrcDesc); + // + // It doesn't enable the BIOSLOCK here. Enable it by policy in DXE. + // Only enable SMI_EN_TCO. + // + PchSetSmiEnTco (); + SmiHandlerProfileRegisterHandler (&gPchTcoSmiDispatchProtocolGuid, (EFI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NULL, 0); + } + return Status; +} + +// +// NEWCENTURY_STS bit that needs to be cleared +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSrcDescNewCentury = { + PCH_SMM_NO_FLAGS, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_EN} + }, + S_ACPI_IO_SMI_EN, + N_ACPI_IO_SMI_EN_TCO + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + TCO_ADDR_TYPE, + {R_TCO_IO_TCO1_STS} + }, + S_TCO_IO_TCO1_STS, + N_TCO_IO_TCO1_STS_NEWCENTURY + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_TCO + } +}; + +/** + The register function used to register SMI handler of NEW CENTURY event. + + @param[in] This The pointer to the protocol itself + @param[in] DispatchFunction Pointer to dispatch function to be invoked for this SMI source + @param[out] DispatchHandle Handle of dispatch function to register. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source description + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database record + @retval EFI_SUCCESS The database record is created successfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock event has been triggered +**/ +EFI_STATUS +EFIAPI +PchTcoSmiNewCenturyRegister ( + IN PCH_TCO_SMI_DISPATCH_PROTOCOL *This, + IN PCH_TCO_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + DATABASE_RECORD *Record; + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock == TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock event has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status = PchSmiRecordInsert ( + &mSrcDescNewCentury, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchTcoSmiNewCenturyType, + DispatchHandle + ); + if (!EFI_ERROR (Status)) { + Record = DATABASE_RECORD_FROM_LINK (*DispatchHandle); + Record->ClearSource = PchTcoSmiClearSourceAndBlock; + PchSmmClearSource (&Record->SrcDesc); + PchSmmEnableSource (&Record->SrcDesc); + SmiHandlerProfileRegisterHandler (&gPchTcoSmiDispatchProtocolGuid, (EFI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NULL, 0); + } + return Status; +} + +/** + Unregister a child SMI source dispatch function with a parent SMM driver + + @param[in] This Protocol instance pointer. + @param[in] DispatchHandle Handle of dispatch function to deregister. + + @retval EFI_SUCCESS The dispatch function has been successfully + unregistered and the SMI source has been disabled + if there are no other registered child dispatch + functions for this SMI source. + @retval EFI_INVALID_PARAMETER Handle is invalid. + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock event has been triggered +**/ +EFI_STATUS +EFIAPI +PchTcoSmiUnRegister ( + IN PCH_TCO_SMI_DISPATCH_PROTOCOL *This, + IN EFI_HANDLE DispatchHandle + ) +{ + return EFI_SUCCESS; +} + +// +// Pme srcdesc +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSrcDescPme = { + PCH_SMM_SCI_EN_DEPENDENT, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_GPE0_EN_127_96} + }, + S_ACPI_IO_GPE0_EN_127_96, + N_ACPI_IO_GPE0_EN_127_96_PME + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_GPE0_STS_127_96} + }, + S_ACPI_IO_GPE0_STS_127_96, + N_ACPI_IO_GPE0_STS_127_96_PME + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_GPE0 + } +}; + +/** + The register function used to register SMI handler of PME event. + + @param[in] This The pointer to the protocol itself + @param[in] DispatchFunction Pointer to dispatch function to be invoked for this SMI source + @param[out] DispatchHandle Handle of dispatch function to register. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source description + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database record + @retval EFI_SUCCESS The database record is created successfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock event has been triggered +**/ +EFI_STATUS +EFIAPI +PchAcpiSmiPmeRegister ( + IN PCH_ACPI_SMI_DISPATCH_PROTOCOL *This, + IN PCH_ACPI_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock == TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock event has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status = PchSmiRecordInsert ( + &mSrcDescPme, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchAcpiSmiPmeType, + DispatchHandle + ); + PchSmmClearSource (&mSrcDescPme); + PchSmmEnableSource (&mSrcDescPme); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileRegisterHandler (&gPchAcpiSmiDispatchProtocolGuid, (EFI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NULL, 0); + } + return Status; +} + +// +// PmeB0 srcdesc +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSrcDescPmeB0 = { + PCH_SMM_SCI_EN_DEPENDENT, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_GPE0_EN_127_96} + }, + S_ACPI_IO_GPE0_EN_127_96, + N_ACPI_IO_GPE0_EN_127_96_PME_B0 + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_GPE0_STS_127_96} + }, + S_ACPI_IO_GPE0_STS_127_96, + N_ACPI_IO_GPE0_STS_127_96_PME_B0 + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_GPE0 + } +}; +/** + The register function used to register SMI handler of PME B0 event. + + @param[in] This The pointer to the protocol itself + @param[in] DispatchFunction Pointer to dispatch function to be invoked for this SMI source + @param[out] DispatchHandle Handle of dispatch function to register. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source description + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database record + @retval EFI_SUCCESS The database record is created successfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock event has been triggered +**/ +EFI_STATUS +EFIAPI +PchAcpiSmiPmeB0Register ( + IN PCH_ACPI_SMI_DISPATCH_PROTOCOL *This, + IN PCH_ACPI_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock == TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock event has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status = PchSmiRecordInsert ( + &mSrcDescPmeB0, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchAcpiSmiPmeB0Type, + DispatchHandle + ); + PchSmmClearSource (&mSrcDescPmeB0); + PchSmmEnableSource (&mSrcDescPmeB0); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileRegisterHandler (&gPchAcpiSmiDispatchProtocolGuid, (EFI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NULL, 0); + } + return Status; +} + +// +// RtcAlarm srcdesc +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSrcDescRtcAlarm = { + PCH_SMM_SCI_EN_DEPENDENT, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_PM1_EN} + }, + S_ACPI_IO_PM1_EN, + N_ACPI_IO_PM1_EN_RTC + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_PM1_STS} + }, + S_ACPI_IO_PM1_STS, + N_ACPI_IO_PM1_STS_RTC + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_PM1_STS_REG + } +}; + +/** + The register function used to register SMI handler of RTC alarm event. + + @param[in] This The pointer to the protocol itself + @param[in] DispatchFunction Pointer to dispatch function to be invoked for this SMI source + @param[out] DispatchHandle Handle of dispatch function to register. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source description + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database record + @retval EFI_SUCCESS The database record is created successfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock event has been triggered +**/ +EFI_STATUS +EFIAPI +PchAcpiSmiRtcAlarmRegister ( + IN PCH_ACPI_SMI_DISPATCH_PROTOCOL *This, + IN PCH_ACPI_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock == TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock event has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status = PchSmiRecordInsert ( + &mSrcDescRtcAlarm, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchAcpiSmiRtcAlarmType, + DispatchHandle + ); + + PchSmmClearSource (&mSrcDescRtcAlarm); + PchSmmEnableSource (&mSrcDescRtcAlarm); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileRegisterHandler (&gPchAcpiSmiDispatchProtocolGuid, (EFI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NULL, 0); + } + return Status; +} + +// +// TmrOverflow srcdesc +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSrcDescTmrOverflow = { + PCH_SMM_SCI_EN_DEPENDENT, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_PM1_EN} + }, + S_ACPI_IO_PM1_EN, + N_ACPI_IO_PM1_EN_TMROF + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_PM1_STS} + }, + S_ACPI_IO_PM1_STS, + N_ACPI_IO_PM1_STS_TMROF + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_PM1_STS_REG + } +}; + +/** + The register function used to register SMI handler of Timer Overflow event. + + @param[in] This The pointer to the protocol itself + @param[in] DispatchFunction Pointer to dispatch function to be invoked for this SMI source + @param[out] DispatchHandle Handle of dispatch function to register. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source description + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database record + @retval EFI_SUCCESS The database record is created successfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock event has been triggered +**/ +EFI_STATUS +EFIAPI +PchAcpiSmiTmrOverflowRegister ( + IN PCH_ACPI_SMI_DISPATCH_PROTOCOL *This, + IN PCH_ACPI_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock == TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock event has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status = PchSmiRecordInsert ( + &mSrcDescTmrOverflow, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchAcpiSmiTmrOverflowType, + DispatchHandle + ); + PchSmmClearSource (&mSrcDescTmrOverflow); + PchSmmEnableSource (&mSrcDescTmrOverflow); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileRegisterHandler (&gPchAcpiSmiDispatchProtocolGuid, (EFI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NULL, 0); + } + + return Status; +} + +/** + Unregister a child SMI source dispatch function with a parent SMM driver + + @param[in] This Protocol instance pointer. + @param[in] DispatchHandle Handle of dispatch function to deregister. + + @retval EFI_SUCCESS The dispatch function has been successfully + unregistered and the SMI source has been disabled + if there are no other registered child dispatch + functions for this SMI source. + @retval EFI_INVALID_PARAMETER Handle is invalid. + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock event has been triggered +**/ +EFI_STATUS +EFIAPI +PchAcpiSmiUnRegister ( + IN PCH_ACPI_SMI_DISPATCH_PROTOCOL *This, + IN EFI_HANDLE DispatchHandle + ) +{ + DATABASE_RECORD *RecordToDelete; + EFI_STATUS Status; + + RecordToDelete = DATABASE_RECORD_FROM_LINK (DispatchHandle); + Status = PchSmmCoreUnRegister (NULL, DispatchHandle); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileUnregisterHandler (&gPchAcpiSmiDispatchProtocolGuid, RecordToDelete->Callback, NULL, 0); + } + return Status; +} + +// +// SerialIrq srcdesc +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSrcDescSerialIrq = { + PCH_SMM_NO_FLAGS, + { + NULL_BIT_DESC_INITIALIZER, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_SERIRQ + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_SERIRQ + } +}; + +/** + The register function used to register SMI handler of Serial IRQ event. + + @param[in] This The pointer to the protocol itself + @param[in] DispatchFunction Pointer to dispatch function to be invoked for this SMI source + @param[out] DispatchHandle Handle of dispatch function to register. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source description + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database record + @retval EFI_SUCCESS The database record is created successfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock event has been triggered +**/ +EFI_STATUS +EFIAPI +PchSmiSerialIrqRegister ( + IN PCH_SMI_DISPATCH_PROTOCOL *This, + IN PCH_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock == TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock event has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status = PchSmiRecordInsert ( + &mSrcDescSerialIrq, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchSmiSerialIrqType, + DispatchHandle + ); + PchSmmClearSource (&mSrcDescSerialIrq); + PchSmmEnableSource (&mSrcDescSerialIrq); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileRegisterHandler (&gPchSmiDispatchProtocolGuid, (EFI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NULL, 0); + } + return Status; +} + +// +// McSmi srcdesc +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSrcDescMcSmi = { + PCH_SMM_NO_FLAGS, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_EN} + }, + S_ACPI_IO_SMI_EN, + N_ACPI_IO_SMI_EN_MCSMI + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_MCSMI + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_MCSMI + } +}; + +/** + The register function used to register SMI handler of MCSMI event. + + @param[in] This The pointer to the protocol itself + @param[in] DispatchFunction Pointer to dispatch function to be invoked for this SMI source + @param[out] DispatchHandle Handle of dispatch function to register. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source description + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database record + @retval EFI_SUCCESS The database record is created successfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock event has been triggered +**/ +EFI_STATUS +EFIAPI +PchSmiMcSmiRegister ( + IN PCH_SMI_DISPATCH_PROTOCOL *This, + IN PCH_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock == TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock event has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status = PchSmiRecordInsert ( + &mSrcDescMcSmi, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchSmiMcSmiType, + DispatchHandle + ); + PchSmmClearSource (&mSrcDescMcSmi); + PchSmmEnableSource (&mSrcDescMcSmi); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileRegisterHandler (&gPchSmiDispatchProtocolGuid, (EFI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NULL, 0); + } + return Status; +} + +// +// SmBus srcdesc +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSrcDescSmbus = { + PCH_SMM_NO_FLAGS, + { + NULL_BIT_DESC_INITIALIZER, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_SMBUS + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_SMBUS + } +}; + +/** + The register function used to register SMI handler of SMBUS event. + + @param[in] This The pointer to the protocol itself + @param[in] DispatchFunction Pointer to dispatch function to be invoked for this SMI source + @param[out] DispatchHandle Handle of dispatch function to register. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source description + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database record + @retval EFI_SUCCESS The database record is created successfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock event has been triggered +**/ +EFI_STATUS +EFIAPI +PchSmiSmbusRegister ( + IN PCH_SMI_DISPATCH_PROTOCOL *This, + IN PCH_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock == TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock event has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status = PchSmiRecordInsert ( + &mSrcDescSmbus, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchSmiSmBusType, + DispatchHandle + ); + PchSmmClearSource (&mSrcDescSmbus); + PchSmmEnableSource (&mSrcDescSmbus); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileRegisterHandler (&gPchSmiDispatchProtocolGuid, (EFI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NULL, 0); + } + return Status; +} + +// +// SpiAsyncSmi srcdesc +// +GLOBAL_REMOVE_IF_UNREFERENCED PCH_SMM_SOURCE_DESC mSrcDescSpiAsyncSmi = { + PCH_SMM_NO_FLAGS, + { + { + { + PCIE_ADDR_TYPE, + { 0xFFFFFFFF } // to be updated in PchSmiDispatchUpdateDescriptors + }, + S_SPI_CFG_BC, + N_SPI_CFG_BC_ASE_BWP + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + PCIE_ADDR_TYPE, + { 0xFFFFFFFF } // to be updated in PchSmiDispatchUpdateDescriptors + }, + S_SPI_CFG_BC, + N_SPI_CFG_BC_ASYNC_SS + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_SPI + } +}; + +/** + Special handling for SPI Asynchronous SMI. + If SPI ASYNC SMI is enabled, De-assert SMI is sent when Flash Cycle Done + transitions from 1 to 0 or when the SMI enable becomes false. + + @param[in] SrcDesc Not used +**/ +VOID +EFIAPI +PchSmiSpiAsyncClearSource ( + CONST PCH_SMM_SOURCE_DESC *SrcDesc + ) +{ + UINT64 SpiRegBase; + UINT32 SpiBar0; + + SpiRegBase = SpiPciCfgBase (); + SpiBar0 = PciSegmentRead32 (SpiRegBase + R_SPI_CFG_BAR0) & ~(B_SPI_CFG_BAR0_MASK); + if (SpiBar0 != PCH_SPI_BASE_ADDRESS) { + // + // Temporary disable MSE, and override with SPI reserved MMIO address, then enable MSE. + // + SpiBar0 = PCH_SPI_BASE_ADDRESS; + PciSegmentAnd8 (SpiRegBase + PCI_COMMAND_OFFSET, (UINT8) ~EFI_PCI_COMMAND_MEMORY_SPACE); + PciSegmentWrite32 (SpiRegBase + R_SPI_CFG_BAR0, SpiBar0); + PciSegmentOr8 (SpiRegBase + PCI_COMMAND_OFFSET, EFI_PCI_COMMAND_MEMORY_SPACE); + } + + MmioOr32 (SpiBar0 + R_SPI_MEM_HSFSC, B_SPI_MEM_HSFSC_FDONE); +} + +/** + Special handling to enable SPI Asynchronous SMI +**/ +VOID +PchSmiSpiAsyncEnableSource ( + VOID + ) +{ + UINT64 SpiRegBase; + UINT32 Data32And; + UINT32 Data32Or; + + SpiRegBase = SpiPciCfgBase (); + Data32And = (UINT32) ~B_SPI_CFG_BC_SYNC_SS; + Data32Or = B_SPI_CFG_BC_ASE_BWP; + + PciSegmentAndThenOr32 ( + SpiRegBase + R_SPI_CFG_BC, + Data32And, + Data32Or + ); + S3BootScriptSavePciCfgReadWrite ( + S3BootScriptWidthUint32, + SpiRegBase + R_SPI_CFG_BC, + (VOID*) &Data32Or, + (VOID*) &Data32And + ); + + // + // Clear the source + // + PchSmiSpiAsyncClearSource (NULL); +} + +/** + The register function used to register SMI handler of SPI Asynchronous event. + + @param[in] This The pointer to the protocol itself + @param[in] DispatchFunction Pointer to dispatch function to be invoked for this SMI source + @param[out] DispatchHandle Handle of dispatch function to register. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source description + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database record + @retval EFI_SUCCESS The database record is created successfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock event has been triggered +**/ +EFI_STATUS +EFIAPI +PchSmiSpiAsyncRegister ( + IN PCH_SMI_DISPATCH_PROTOCOL *This, + IN PCH_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + DATABASE_RECORD *Record; + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock == TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock event has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status = PchSmiRecordInsert ( + &mSrcDescSpiAsyncSmi, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchSmiSpiAsyncType, + DispatchHandle + ); + + if (!EFI_ERROR (Status)) { + Record = DATABASE_RECORD_FROM_LINK (*DispatchHandle); + Record->ClearSource = PchSmiSpiAsyncClearSource; + PchSmiSpiAsyncClearSource (NULL); + PchSmiSpiAsyncEnableSource (); + SmiHandlerProfileRegisterHandler (&gPchSmiDispatchProtocolGuid, (EFI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NULL, 0); + } + return Status; +} + +/** + Unregister a child SMI source dispatch function with a parent SMM driver + + @param[in] This Protocol instance pointer. + @param[in] DispatchHandle Handle of dispatch function to deregister. + + @retval EFI_SUCCESS The dispatch function has been successfully + unregistered and the SMI source has been disabled + if there are no other registered child dispatch + functions for this SMI source. + @retval EFI_INVALID_PARAMETER Handle is invalid. + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock event has been triggered + @retval EFI_ACCESS_DENIED Return access denied since SPI aync SMI handler is not able to disabled. +**/ +EFI_STATUS +EFIAPI +PchSmiUnRegister ( + IN PCH_SMI_DISPATCH_PROTOCOL *This, + IN EFI_HANDLE DispatchHandle + ) +{ + DATABASE_RECORD *Record; + UINT64 SpiRegBase; + EFI_STATUS Status; + + Record = DATABASE_RECORD_FROM_LINK (DispatchHandle); + if ((Record->SrcDesc.En[0].Reg.Type == PCIE_ADDR_TYPE) && + (Record->SrcDesc.En[0].Reg.Data.pcie.Fields.Dev == SpiDevNumber ()) && + (Record->SrcDesc.En[0].Reg.Data.pcie.Fields.Fnc == SpiFuncNumber ()) && + (Record->SrcDesc.En[0].Reg.Data.pcie.Fields.Reg == R_SPI_CFG_BC) && + (Record->SrcDesc.En[0].Bit == N_SPI_CFG_BC_ASE_BWP)) { + SpiRegBase = SpiPciCfgBase (); + if (PciSegmentRead8 (SpiRegBase + R_SPI_CFG_BC) & B_SPI_CFG_BC_BILD) { + // + // SPI Asynchronous SMI cannot be disabled + // + return EFI_ACCESS_DENIED; + } + } + Status = PchSmmCoreUnRegister (NULL, DispatchHandle); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileUnregisterHandler (&gPchSmiDispatchProtocolGuid, Record->Callback, NULL, 0); + } + return Status; +} + + +/** + Declaration of PCH TCO SMI DISPATCH PROTOCOL instance +**/ +PCH_TCO_SMI_DISPATCH_PROTOCOL mPchTcoSmiDispatchProtocol = { + PCH_TCO_SMI_DISPATCH_REVISION, // Revision + PchTcoSmiUnRegister, // Unregister + PchTcoSmiMchRegister, // Mch + PchTcoSmiTcoTimeoutRegister, // TcoTimeout + PchTcoSmiOsTcoRegister, // OsTco + PchTcoSmiNmiRegister, // Nmi + PchTcoSmiIntruderDetRegister, // IntruderDectect + PchTcoSmiSpiBiosWpRegister, // SpiBiosWp + PchTcoSmiLpcBiosWpRegister, // LpcBiosWp + PchTcoSmiNewCenturyRegister // NewCentury +}; + +/** + Declaration of PCH ACPI SMI DISPATCH PROTOCOL instance +**/ +PCH_ACPI_SMI_DISPATCH_PROTOCOL mPchAcpiSmiDispatchProtocol = { + PCH_ACPI_SMI_DISPATCH_REVISION, // Revision + PchAcpiSmiUnRegister, // Unregister + PchAcpiSmiPmeRegister, // Pme + PchAcpiSmiPmeB0Register, // PmeB0 + PchAcpiSmiRtcAlarmRegister, // RtcAlarm + PchAcpiSmiTmrOverflowRegister // TmrOverflow +}; + +/** + Declaration of MISC PCH SMI DISPATCH PROTOCOL instance +**/ +PCH_SMI_DISPATCH_PROTOCOL mPchSmiDispatchProtocol = { + PCH_SMI_DISPATCH_REVISION, // Revision + PchSmiUnRegister, // Unregister + PchSmiSerialIrqRegister, // SerialIrq + PchSmiMcSmiRegister, // McSmi + PchSmiSmbusRegister, // SmBus + PchSmiSpiAsyncRegister // SpiAsync +}; + +/** + Install protocols of PCH specifics SMI types, including + PCH TCO SMI types, PCH PCIE SMI types, PCH ACPI SMI types, PCH MISC SMI types. + + @retval the result of protocol installation +**/ +EFI_STATUS +InstallPchSmiDispatchProtocols ( + VOID + ) +{ + EFI_HANDLE Handle; + EFI_STATUS Status; + + Handle = NULL; + Status = gSmst->SmmInstallProtocolInterface ( + &Handle, + &gPchTcoSmiDispatchProtocolGuid, + EFI_NATIVE_INTERFACE, + &mPchTcoSmiDispatchProtocol + ); + + Status = gSmst->SmmInstallProtocolInterface ( + &Handle, + &gPchAcpiSmiDispatchProtocolGuid, + EFI_NATIVE_INTERFACE, + &mPchAcpiSmiDispatchProtocol + ); + Status = gSmst->SmmInstallProtocolInterface ( + &Handle, + &gPchSmiDispatchProtocolGuid, + EFI_NATIVE_INTERFACE, + &mPchSmiDispatchProtocol + ); + + return Status; +} + +/** + The function to dispatch all callback function of PCH SMI types. + + @retval EFI_SUCCESS Function successfully completed + @retval EFI_UNSUPPORTED no +**/ +EFI_STATUS +PchSmiTypeCallbackDispatcher ( + IN DATABASE_RECORD *Record + ) +{ + EFI_STATUS Status; + PCH_SMI_TYPES PchSmiType; + + PchSmiType = Record->PchSmiType; + Status = EFI_SUCCESS; + + switch (PchSmiType) { + case PchTcoSmiMchType: + case PchTcoSmiTcoTimeoutType: + case PchTcoSmiOsTcoType: + case PchTcoSmiNmiType: + case PchTcoSmiIntruderDetectType: + case PchTcoSmiSpiBiosWpType: + case PchTcoSmiLpcBiosWpType: + case PchTcoSmiNewCenturyType: + ((PCH_TCO_SMI_DISPATCH_CALLBACK) (Record->PchSmiCallback)) ((EFI_HANDLE)&Record->Link); + break; + case PchPcieSmiRpHotplugType: + case PchPcieSmiRpLinkActiveType: + case PchPcieSmiRpLinkEqType: + break; + case PchAcpiSmiPmeType: + case PchAcpiSmiPmeB0Type: + case PchAcpiSmiRtcAlarmType: + case PchAcpiSmiTmrOverflowType: + ((PCH_ACPI_SMI_DISPATCH_CALLBACK) (Record->PchSmiCallback)) ((EFI_HANDLE)&Record->Link); + break; + case PchSmiSerialIrqType: + case PchSmiMcSmiType: + case PchSmiSmBusType: + case PchSmiSpiAsyncType: + case PchIoTrapSmiType: ///< internal type for IoTrap + ((PCH_SMI_DISPATCH_CALLBACK) (Record->PchSmiCallback)) ((EFI_HANDLE)&Record->Link); + break; + default: + Status = EFI_UNSUPPORTED; + break; + } + + return Status; +} + +/** + Performs update of SmiDispatch descriptors with values that have to be evaluated during runtime. +**/ +VOID +PchSmiDispatchUpdateDescriptors ( + VOID + ) +{ + UINT32 SpiPcieAddr; + UINT32 LpcPcieAddr; + + SpiPcieAddr = ((DEFAULT_PCI_BUS_NUMBER_PCH << 24) | + (SpiDevNumber () << 19) | + (SpiFuncNumber () << 16) | + R_SPI_CFG_BC); + LpcPcieAddr = ((DEFAULT_PCI_BUS_NUMBER_PCH << 24) | + (LpcDevNumber () << 19) | + (LpcFuncNumber () << 16) | + R_LPC_CFG_BC); + // + // mSrcDescSpiBiosWp + // + mSrcDescSpiBiosWp.En[1].Reg.Data.raw = SpiPcieAddr; + mSrcDescSpiBiosWp.Sts[0].Reg.Data.raw = SpiPcieAddr; + + // + // mSrcDescLpcBiosWp + // + mSrcDescLpcBiosWp.En[1].Reg.Data.raw = LpcPcieAddr; + + // + // mSrcDescSpiAsyncSmi + // + mSrcDescSpiAsyncSmi.En[0].Reg.Data.raw = SpiPcieAddr; + mSrcDescSpiAsyncSmi.Sts[0].Reg.Data.raw = SpiPcieAddr; +} \ No newline at end of file diff --git a/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmiDispatcher.inf b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmiDispatcher.inf new file mode 100644 index 0000000000..7031b5193a --- /dev/null +++ b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmiDispatcher.inf @@ -0,0 +1,106 @@ +## @file +# Component description file for the Pch SMI Dispatch Handlers module +# +# Copyright (c) 2022, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +## + + +[Defines] +INF_VERSION = 0x00010017 +BASE_NAME = PchSmiDispatcher +FILE_GUID = B0D6ED53-B844-43f5-BD2F-61095264E77E +VERSION_STRING = 1.0 +MODULE_TYPE = DXE_SMM_DRIVER +PI_SPECIFICATION_VERSION = 1.10 +ENTRY_POINT = InitializePchSmmDispatcher + + +[LibraryClasses] +UefiBootServicesTableLib +UefiDriverEntryPoint +IoLib +DebugLib +PcdLib +BaseLib +BaseMemoryLib +HobLib +DevicePathLib +PchCycleDecodingLib +PchPcieRpLib +PchPcrLib +SmmServicesTableLib +ReportStatusCodeLib +PerformanceLib +DxeServicesTableLib +GpioLib +GpioPrivateLib +EspiLib +S3BootScriptLib +ConfigBlockLib +PmcPrivateLib +PmcLib +SmiHandlerProfileLib +PchPciBdfLib +P2SbSidebandAccessLib +CpuPcieInfoFruLib + +[Packages] +MdePkg/MdePkg.dec +AlderlakeSiliconPkg/SiPkg.dec + + +[Pcd] +# Progress Code for S3 Suspend start. +# PROGRESS_CODE_S3_SUSPEND_START = (EFI_SOFTWARE_SMM_DRIVER | (EFI_OEM_SPECIFIC | 0x00000000)) = 0x03078000 +gSiPkgTokenSpaceGuid.PcdProgressCodeS3SuspendStart +# Progress Code for S3 Suspend end. +# PROGRESS_CODE_S3_SUSPEND_END = (EFI_SOFTWARE_SMM_DRIVER | (EFI_OEM_SPECIFIC | 0x00000001)) = 0x03078001 +gSiPkgTokenSpaceGuid.PcdProgressCodeS3SuspendEnd +gSiPkgTokenSpaceGuid.PcdEfiGcdAllocateType + + +[Sources] +PchSmm.h +PchSmmCore.c +PchSmmHelpers.c +PchxSmmHelpers.c +SmmGlobalsPch.c +PchSmmUsb.c +PchSmmGpi.c +PchSmmPowerButton.c +PchSmmSw.c +PchSmmSx.c +PchSmmPeriodicTimer.c +PchSmiDispatch.c +PcieSmmClient.c + + +[Protocols] +gEfiPciRootBridgeIoProtocolGuid ## CONSUMES +gEfiSmmGpiDispatch2ProtocolGuid ## PRODUCES +gEfiSmmSxDispatch2ProtocolGuid ## PRODUCES +gEfiSmmSwDispatch2ProtocolGuid ## PRODUCES +gEfiSmmUsbDispatch2ProtocolGuid ## PRODUCES +gEfiSmmPowerButtonDispatch2ProtocolGuid ## PRODUCES +gEfiSmmPeriodicTimerDispatch2ProtocolGuid ## PRODUCES +gEfiSmmBase2ProtocolGuid ## CONSUMES +gEfiSmmCpuProtocolGuid ## CONSUMES +gEfiSmmReadyToLockProtocolGuid ## CONSUMES +gPchTcoSmiDispatchProtocolGuid ## PRODUCES +gPchPcieSmiDispatchProtocolGuid ## PRODUCES +gPchAcpiSmiDispatchProtocolGuid ## PRODUCES +gPchSmiDispatchProtocolGuid ## PRODUCES +gPchEspiSmiDispatchProtocolGuid ## PRODUCES +gPchSmmPeriodicTimerControlGuid ## PRODUCES + + +[Guids] + + +[Depex] +gEfiPciRootBridgeIoProtocolGuid AND +gEfiPciHostBridgeResourceAllocationProtocolGuid AND ## This is to ensure that PCI MMIO resource has been prepared and available for this driver to allocate. +gEfiSmmCpuProtocolGuid AND +gEfiSmmBase2ProtocolGuid + diff --git a/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmiHelper.h b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmiHelper.h new file mode 100644 index 0000000000..5ec9455797 --- /dev/null +++ b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmiHelper.h @@ -0,0 +1,34 @@ +/** @file + eSPI SMI Dispatch header + + Copyright (c) 2022, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef _PCH_SMI_HELPER_H_ +#define _PCH_SMI_HELPER_H_ +#include + +/** + Get CPU or PCH Pcie Root Port Device and Function Number by Root Port physical Number + + @param[in] RpNumber Root port physical number. (0-based) + @param[out] RpDev Return corresponding root port device number. + @param[out] RpFun Return corresponding root port function number. +**/ +VOID +GetPcieRpDevFun ( + IN UINTN RpIndex, + OUT UINTN *RpDev, + OUT UINTN *RpFun + ); + +/** + Performs update of SmiDispatch descriptors with values that have to be evaluated during runtime. +**/ +VOID +PchSmiDispatchUpdateDescriptors ( + VOID + ); + +#endif diff --git a/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmm.h b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmm.h new file mode 100644 index 0000000000..41969a0ec7 --- /dev/null +++ b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmm.h @@ -0,0 +1,1028 @@ +/** @file + Prototypes and defines for the PCH SMM Dispatcher. + + Copyright (c) 2022, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ +#ifndef _PCH_SMM_H_ +#define _PCH_SMM_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define EFI_BAD_POINTER 0xAFAFAFAFAFAFAFAFULL + +extern BOOLEAN mReadyToLock; + +/// +/// Define an enumeration for all the supported protocols +/// +#define PCH_SMM_PROTOCOL_TYPE_MAX 6 + +typedef enum { + UsbType, + SxType, + SwType, + GpiType, + PowerButtonType, + PeriodicTimerType, + PchSmiDispatchType, + PchSmmProtocolTypeMax +} PCH_SMM_PROTOCOL_TYPE; + +/// +/// Define all the supported types of PCH SMI +/// +typedef enum { + PchTcoSmiMchType, + PchTcoSmiTcoTimeoutType, + PchTcoSmiOsTcoType, + PchTcoSmiNmiType, + PchTcoSmiIntruderDetectType, + PchTcoSmiSpiBiosWpType, + PchTcoSmiLpcBiosWpType, + PchTcoSmiNewCenturyType, + PchPcieSmiRpHotplugType, + PchPcieSmiRpLinkActiveType, + PchPcieSmiRpLinkEqType, + PchAcpiSmiPmeType, + PchAcpiSmiPmeB0Type, + PchAcpiSmiRtcAlarmType, + PchAcpiSmiTmrOverflowType, + PchEspiSmiEspiSlaveType, + PchSmiSerialIrqType, + PchSmiMcSmiType, + PchSmiSmBusType, + PchSmiSpiAsyncType, + PchIoTrapSmiType ///< internal SMI type +} PCH_SMI_TYPES; + +/// +/// Generic funciton pointer to cover all Pch SMI function pointer types +/// +typedef +VOID +(EFIAPI *PCH_SMI_CALLBACK_FUNCTIONS) ( + IN EFI_HANDLE DispatchHandle, + ... + ); + + +/// +/// SPECIFYING A REGISTER +/// We want a general way of referring to addresses. For this case, we'll only +/// need addresses in the ACPI table (and the TCO entries within the ACPI table). +/// However, it's interesting to consider what it would take to support other types +/// of addresses. To address Will's concern, I think it prudent to accommodate it +/// early on in the design. +/// +/// Addresses we need to consider: +/// +/// Type: Required: +/// I/O Yes +/// ACPI (special case of I/O) Only if we want to +/// TCO (special case of I/O) Only if we want to +/// GPIO (special case of MMIO) Only if we want to +/// Memory (or Memory Mapped I/O) Only if we want to +/// PCIE Yes, for BiosWp +/// +typedef enum { + /// + /// IO_ADDR_TYPE, /// unimplemented + /// + ACPI_ADDR_TYPE, + TCO_ADDR_TYPE, + /// + /// MEMORY_ADDR_TYPE, /// unimplemented + /// + GPIO_ADDR_TYPE, + MEMORY_MAPPED_IO_ADDRESS_TYPE, + PCIE_ADDR_TYPE, + PCR_ADDR_TYPE, + NUM_ADDR_TYPES, ///< count of items in this enum + PCH_SMM_ADDR_TYPE_NULL = -1 ///< sentinel to indicate NULL or to signal end of arrays +} ADDR_TYPE; + +// +// Assumption: 32-bits -- enum's evaluate to integer +// Assumption: This code will only run on IA-32. Justification: IA-64 doesn't have SMIs. +// We don't have to worry about 64-bit addresses. +// Typedef the size of addresses in case the numbers I'm using are wrong or in case +// this changes. This is a good idea because PCI_ADDR will change, for example, when +// we add support for PciExpress. +// +typedef UINT16 IO_ADDR; +typedef IO_ADDR ACPI_ADDR; ///< can omit +typedef IO_ADDR TCO_ADDR; ///< can omit +typedef UINTN MEM_ADDR; +typedef MEM_ADDR *MEMORY_MAPPED_IO_ADDRESS; +typedef MEM_ADDR *GPIO_ADDR; +typedef union { + UINT32 Raw; + struct { + UINT32 Reg: 16; + UINT32 Fnc: 3; + UINT32 Dev: 5; + UINT32 Bus: 8; + } Fields; +} PCIE_ADDR; + +typedef union { + UINT32 Raw; + struct { + UINT16 Offset; + UINT8 Pid; + UINT8 Base; + } Fields; +} PCR_ADDR; + +typedef struct { + ADDR_TYPE Type; + union { + /// + /// used to initialize during declaration/definition + /// + UINT32 raw; + + /// + /// used to access useful data + /// + IO_ADDR io; + ACPI_ADDR acpi; + TCO_ADDR tco; + GPIO_ADDR gpio; + MEM_ADDR mem; + MEMORY_MAPPED_IO_ADDRESS Mmio; + PCIE_ADDR pcie; + PCR_ADDR Pcr; + + } Data; + +} PCH_SMM_ADDRESS; + +/// +/// SPECIFYING BITS WITHIN A REGISTER +/// Here's a struct that helps us specify a source or enable bit. +/// +typedef struct { + PCH_SMM_ADDRESS Reg; + UINT8 SizeInBytes; ///< of the register + UINT8 Bit; +} PCH_SMM_BIT_DESC; + +// +// Sometimes, we'll have bit descriptions that are unused. It'd be great to have a +// way to easily identify them: +// +#define IS_BIT_DESC_NULL(BitDesc) ((BitDesc).Reg.Type == PCH_SMM_ADDR_TYPE_NULL) ///< "returns" true when BitDesc is NULL +#define NULL_THIS_BIT_DESC(BitDesc) ((BitDesc).Reg.Type = PCH_SMM_ADDR_TYPE_NULL) ///< will "return" an integer w/ value of 0 +#define NULL_BIT_DESC_INITIALIZER \ + { \ + { \ + PCH_SMM_ADDR_TYPE_NULL, \ + { \ + 0 \ + } \ + }, \ + 0, 0 \ + } +// +// I'd like a type to specify the callback's Sts & En bits because they'll +// be commonly used together: +// +#define NUM_EN_BITS 2 +#define NUM_STS_BITS 1 + +// +// Flags +// +typedef UINT8 PCH_SMM_SOURCE_FLAGS; + +// +// Flags required to describe the event source +// +#define PCH_SMM_NO_FLAGS 0 +#define PCH_SMM_SCI_EN_DEPENDENT 1 + +typedef struct { + PCH_SMM_SOURCE_FLAGS Flags; + PCH_SMM_BIT_DESC En[NUM_EN_BITS]; ///< Describes the enable bit(s) for the SMI event + PCH_SMM_BIT_DESC Sts[NUM_STS_BITS]; ///< Describes the secondary status bit for the SMI event. Might be the same as TopLevelSmi + PCH_SMM_BIT_DESC PmcSmiSts; ///< Refereing to the top level status bit in PMC SMI_STS, i.e. R_PCH_SMI_STS +} PCH_SMM_SOURCE_DESC; + +/// +/// Used to initialize null source descriptor +/// +#define NULL_SOURCE_DESC_INITIALIZER \ + { \ + PCH_SMM_NO_FLAGS, \ + { \ + NULL_BIT_DESC_INITIALIZER, NULL_BIT_DESC_INITIALIZER \ + }, \ + { \ + NULL_BIT_DESC_INITIALIZER \ + }, \ + NULL_BIT_DESC_INITIALIZER \ + } + +/// +/// Define a PCIE RP event context for SmiProfileHandlerInfo tool +/// +typedef struct { + PCH_SMI_TYPES PchSmiType; + UINTN RpIndex; +} PCH_SMM_PCIE_REGISTER_CONTEXT; + +/// +/// CHILD CONTEXTS +/// To keep consistent w/ the architecture, we'll need to provide the context +/// to the child when we call its callback function. After talking with Will, +/// we agreed that we'll need functions to "dig" the context out of the hardware +/// in many cases (Sx, Trap, Gpi, etc), and we'll need a function to compare those +/// contexts to prevent unnecessary dispatches. I'd like a general type for these +/// "GetContext" functions, so I'll need a union of all the protocol contexts for +/// our internal use: +/// +typedef union { + // + // (in no particular order) + // + EFI_SMM_SX_REGISTER_CONTEXT Sx; + EFI_SMM_PERIODIC_TIMER_REGISTER_CONTEXT PeriodicTimer; + EFI_SMM_SW_REGISTER_CONTEXT Sw; + EFI_SMM_POWER_BUTTON_REGISTER_CONTEXT PowerButton; + EFI_SMM_USB_REGISTER_CONTEXT Usb; + EFI_SMM_GPI_REGISTER_CONTEXT Gpi; + PCH_SMM_PCIE_REGISTER_CONTEXT Pcie; +} PCH_SMM_CONTEXT; + +/// +/// Misc data for PchDispatcher usage. +/// For PeriodicTimer, since the ElapsedTime is removed from EFI_SMM_PERIODIC_TIMER_REGISTER_CONTEXT of EDKII, +/// and PchDispatcher needs it for every record. Thus move it here to support ElapsedTime. +/// +typedef struct { + UINTN ElapsedTime; + /// + /// A switch to control periodic timer SMI enabling + /// + BOOLEAN TimerSmiEnabled; +} PCH_SMM_MISC_DATA; + +// +// Assumption: PeriodicTimer largest at 3x64-bits or 24 bytes +// +typedef struct _DATABASE_RECORD DATABASE_RECORD; + +/// +/// Assumption: the GET_CONTEXT function will be as small and simple as possible. +/// Assumption: We don't need to pass in an enumeration for the protocol because each +/// GET_CONTEXT function is written for only one protocol. +/// We also need a function to compare contexts to see if the child should be dispatched +/// In addition, we need a function to acquire CommBuffer and CommBufferSize for +/// dispatch callback function of EDKII native support. +/// +typedef +VOID +(EFIAPI *GET_CONTEXT) ( + IN DATABASE_RECORD * Record, + OUT PCH_SMM_CONTEXT * Context + ); + +typedef +BOOLEAN +(EFIAPI *CMP_CONTEXT) ( + IN PCH_SMM_CONTEXT * Context1, + IN PCH_SMM_CONTEXT * Context2 + ); + +typedef +VOID +(EFIAPI *GET_COMMBUFFER) ( + IN DATABASE_RECORD * Record, + OUT VOID **CommBuffer, + OUT UINTN * CommBufferSize + ); + +/// +/// Finally, every protocol will require a "Get Context" and "Compare Context" call, so +/// we may as well wrap that up in a table, too. +/// +typedef struct { + GET_CONTEXT GetContext; + CMP_CONTEXT CmpContext; + GET_COMMBUFFER GetCommBuffer; +} CONTEXT_FUNCTIONS; + +extern CONTEXT_FUNCTIONS ContextFunctions[PCH_SMM_PROTOCOL_TYPE_MAX]; + +/// +/// MAPPING CONTEXT TO BIT DESCRIPTIONS +/// I'd like to have a general approach to mapping contexts to bit descriptions. +/// Sometimes, we'll find that we can use table lookups or constant assignments; +/// other times, we'll find that we'll need to use a function to perform the mapping. +/// If we define a macro to mask that process, we'll never have to change the code. +/// I don't know if this is desirable or not -- if it isn't, then we can get rid +/// of the macros and just use function calls or variable assignments. Doesn't matter +/// to me. +/// Mapping complex contexts requires a function +/// + +/** + Maps a USB context to a source description. + + @param[in] Context The context we need to map. Type must be USB. + @param[out] SrcDesc The source description that corresponds to the given context. + +**/ +VOID +MapUsbToSrcDesc ( + IN PCH_SMM_CONTEXT *Context, + OUT PCH_SMM_SOURCE_DESC *SrcDesc + ); + +/** + Figure out which timer the child is requesting and + send back the source description + + @param[in] DispatchContext The pointer to the Dispatch Context instances + @param[out] SrcDesc The pointer to the source description + +**/ +VOID +MapPeriodicTimerToSrcDesc ( + IN PCH_SMM_CONTEXT *DispatchContext, + OUT PCH_SMM_SOURCE_DESC *SrcDesc + ); + +// +// Mapping simple contexts can be done by assignment or lookup table +// +extern CONST PCH_SMM_SOURCE_DESC mSxSourceDesc; +extern CONST PCH_SMM_SOURCE_DESC mPowerButtonSourceDesc; +extern CONST PCH_SMM_SOURCE_DESC mSrcDescNewCentury; +extern CONST PCH_SMM_SOURCE_DESC mGpiSourceDescTemplate; + +/// +/// For PCHx, APMC is UINT8 port, so the MAX SWI Value is 0xFF. +/// +#define MAXIMUM_SWI_VALUE 0xFF +/// +/// Open: Need to make sure this kind of type cast will actually work. +/// May need an intermediate form w/ two VOID* arguments. I'll figure +/// that out when I start compiling. +/// +typedef +VOID +(EFIAPI *PCH_SMM_CLEAR_SOURCE) ( + CONST PCH_SMM_SOURCE_DESC * SrcDesc + ); + +/// +/// "DATABASE" RECORD +/// Linked list data structures +/// +#define DATABASE_RECORD_SIGNATURE SIGNATURE_32 ('D', 'B', 'R', 'C') + +struct _DATABASE_RECORD { + UINT32 Signature; + LIST_ENTRY Link; + BOOLEAN Processed; + /// + /// Status and Enable bit description + /// + PCH_SMM_SOURCE_DESC SrcDesc; + + /// + /// Callback function + /// + EFI_SMM_HANDLER_ENTRY_POINT2 Callback; + PCH_SMM_CONTEXT ChildContext; + UINTN ContextSize; + + /// + /// Special handling hooks -- init them to NULL if unused/unneeded + /// + PCH_SMM_CLEAR_SOURCE ClearSource; + + /// + /// Functions required to make callback code general + /// + CONTEXT_FUNCTIONS ContextFunctions; + + /// + /// The protocol that this record dispatches + /// + PCH_SMM_PROTOCOL_TYPE ProtocolType; + + /// + /// Misc data for private usage + /// + PCH_SMM_MISC_DATA MiscData; + + /// + /// PCH SMI callback function + /// + PCH_SMI_CALLBACK_FUNCTIONS PchSmiCallback; + /// + /// Indicate the PCH SMI types. + /// + PCH_SMI_TYPES PchSmiType; +}; + +#define DATABASE_RECORD_FROM_LINK(_record) CR (_record, DATABASE_RECORD, Link, DATABASE_RECORD_SIGNATURE) +#define DATABASE_RECORD_FROM_CHILDCONTEXT(_record) CR (_record, DATABASE_RECORD, ChildContext, DATABASE_RECORD_SIGNATURE) + +/// +/// HOOKING INTO THE ARCHITECTURE +/// +typedef +EFI_STATUS +(EFIAPI *PCH_SMM_GENERIC_REGISTER) ( + IN VOID **This, + IN VOID *DispatchFunction, + IN VOID *DispatchContext, + OUT EFI_HANDLE *DispatchHandle + ); +typedef +EFI_STATUS +(EFIAPI *PCH_SMM_GENERIC_UNREGISTER) ( + IN VOID **This, + IN EFI_HANDLE DispatchHandle + ); + +/// +/// Define a memory "stamp" equivalent in size and function to most of the protocols +/// +typedef struct { + PCH_SMM_GENERIC_REGISTER Register; + PCH_SMM_GENERIC_UNREGISTER Unregister; + UINTN Extra1; + UINTN Extra2; ///< may not need this one +} PCH_SMM_GENERIC_PROTOCOL; + +/** + Register a child SMI dispatch function with a parent SMM driver. + + @param[in] This Pointer to the PCH_SMM_GENERIC_PROTOCOL instance. + @param[in] DispatchFunction Pointer to dispatch function to be invoked for this SMI source. + @param[in] DispatchContext Pointer to the dispatch function's context. + @param[out] DispatchHandle Handle of dispatch function, for when interfacing + with the parent SMM driver, will be the address of linked + list link in the call back record. + + @retval EFI_OUT_OF_RESOURCES Insufficient resources to create database record + @retval EFI_INVALID_PARAMETER The input parameter is invalid + @retval EFI_SUCCESS The dispatch function has been successfully + registered and the SMI source has been enabled. +**/ +EFI_STATUS +EFIAPI +PchPiSmmCoreRegister ( + IN PCH_SMM_GENERIC_PROTOCOL *This, + IN EFI_SMM_HANDLER_ENTRY_POINT2 DispatchFunction, + IN PCH_SMM_CONTEXT *DispatchContext, + OUT EFI_HANDLE *DispatchHandle + ); + +/** + Unregister a child SMI source dispatch function with a parent SMM driver + + @param[in] This Protocol instance pointer. + @param[in] DispatchHandle Handle of dispatch function to deregister. + + @retval EFI_SUCCESS The dispatch function has been successfully + unregistered and the SMI source has been disabled + if there are no other registered child dispatch + functions for this SMI source. + @retval EFI_INVALID_PARAMETER Handle is invalid. + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock event has been triggered +**/ +EFI_STATUS +EFIAPI +PchPiSmmCoreUnRegister ( + IN PCH_SMM_GENERIC_PROTOCOL *This, + IN EFI_HANDLE *DispatchHandle + ); + + +/** + Unregister a child SMI source dispatch function with a parent SMM driver. + + @param[in] This Pointer to the PCH_SMM_GENERIC_PROTOCOL instance. + @param[in] DispatchHandle Handle of dispatch function to deregister. + + @retval EFI_SUCCESS The dispatch function has been successfully + unregistered and the SMI source has been disabled + if there are no other registered child dispatch + functions for this SMI source. + @retval EFI_INVALID_PARAMETER Handle is invalid. +**/ +EFI_STATUS +EFIAPI +PchSmmCoreUnRegister ( + IN PCH_SMM_GENERIC_PROTOCOL *This, + IN EFI_HANDLE *DispatchHandle + ); + +typedef union { + PCH_SMM_GENERIC_PROTOCOL Generic; + EFI_SMM_USB_DISPATCH2_PROTOCOL Usb; + EFI_SMM_SX_DISPATCH2_PROTOCOL Sx; + EFI_SMM_SW_DISPATCH2_PROTOCOL Sw; + EFI_SMM_GPI_DISPATCH2_PROTOCOL Gpi; + EFI_SMM_POWER_BUTTON_DISPATCH2_PROTOCOL PowerButton; + EFI_SMM_PERIODIC_TIMER_DISPATCH2_PROTOCOL PeriodicTimer; +} PCH_SMM_PROTOCOL; + +/// +/// Define a structure to help us identify the generic protocol +/// +#define PROTOCOL_SIGNATURE SIGNATURE_32 ('P', 'R', 'O', 'T') + +typedef struct { + UINTN Signature; + + PCH_SMM_PROTOCOL_TYPE Type; + EFI_GUID *Guid; + PCH_SMM_PROTOCOL Protocols; +} PCH_SMM_QUALIFIED_PROTOCOL; + +#define QUALIFIED_PROTOCOL_FROM_GENERIC(_generic) \ + CR ( \ + _generic, \ + PCH_SMM_QUALIFIED_PROTOCOL, \ + Protocols, \ + PROTOCOL_SIGNATURE \ + ) + +/// +/// Create private data for the protocols that we'll publish +/// +typedef struct { + LIST_ENTRY CallbackDataBase; + EFI_HANDLE SmiHandle; + EFI_HANDLE InstallMultProtHandle; + PCH_SMM_QUALIFIED_PROTOCOL Protocols[PCH_SMM_PROTOCOL_TYPE_MAX]; +} PRIVATE_DATA; + +extern PRIVATE_DATA mPrivateData; +extern UINT16 mAcpiBaseAddr; +extern UINT16 mTcoBaseAddr; + +/** + The internal function used to create and insert a database record + + @param[in] InsertRecord Record to insert to database. + @param[out] DispatchHandle Handle of dispatch function to register. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source description + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database record + @retval EFI_SUCCESS The database record is created successfully. +**/ +EFI_STATUS +SmmCoreInsertRecord ( + IN DATABASE_RECORD *NewRecord, + OUT EFI_HANDLE *DispatchHandle + ); + +/** + Get the Sleep type + + @param[in] Record No use + @param[out] Context The context that includes SLP_TYP bits to be filled +**/ +VOID +EFIAPI +SxGetContext ( + IN DATABASE_RECORD *Record, + OUT PCH_SMM_CONTEXT *Context + ); + +/** + 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 RegisterContext->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] RegisterContext 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 a valid range or is already in use. + @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 +PchSwSmiRegister ( + IN EFI_SMM_SW_DISPATCH2_PROTOCOL *This, + IN EFI_SMM_HANDLER_ENTRY_POINT2 DispatchFunction, + IN EFI_SMM_SW_REGISTER_CONTEXT *DispatchContext, + OUT EFI_HANDLE *DispatchHandle + ); + +/** + 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 +PchSwSmiUnRegister ( + IN CONST EFI_SMM_SW_DISPATCH2_PROTOCOL *This, + IN EFI_HANDLE DispatchHandle + ); + +/** + Init required protocol for Pch Sw Dispatch protocol. +**/ +VOID +PchSwDispatchInit ( + VOID + ); + +/** + Check whether sleep type of two contexts match + + @param[in] Context1 Context 1 that includes sleep type 1 + @param[in] Context2 Context 2 that includes sleep type 2 + + @retval FALSE Sleep types match + @retval TRUE Sleep types don't match +**/ +BOOLEAN +EFIAPI +SxCmpContext ( + IN PCH_SMM_CONTEXT *Context1, + IN PCH_SMM_CONTEXT *Context2 + ); + +/** + Update the elapsed time from the Interval data of DATABASE_RECORD + + @param[in] Record The pointer to the DATABASE_RECORD. + @param[out] HwContext The Context to be updated. +**/ +VOID +EFIAPI +PeriodicTimerGetContext ( + IN DATABASE_RECORD *Record, + OUT PCH_SMM_CONTEXT *Context + ); + +/** + Check whether Periodic Timer of two contexts match + + @param[in] Context1 Context 1 that includes Periodic Timer 1 + @param[in] Context2 Context 2 that includes Periodic Timer 2 + + @retval FALSE Periodic Timer match + @retval TRUE Periodic Timer don't match +**/ +BOOLEAN +EFIAPI +PeriodicTimerCmpContext ( + IN PCH_SMM_CONTEXT *Context1, + IN PCH_SMM_CONTEXT *Context2 + ); + +/** + Gather the CommBuffer information of SmmPeriodicTimerDispatch2. + + @param[in] Record No use + @param[out] CommBuffer Point to the CommBuffer structure + @param[out] CommBufferSize Point to the Size of CommBuffer structure +**/ +VOID +EFIAPI +PeriodicTimerGetCommBuffer ( + IN DATABASE_RECORD *Record, + OUT VOID **CommBuffer, + OUT UINTN *CommBufferSize + ); + +/** + Get the power button status. + + @param[in] Record The pointer to the DATABASE_RECORD. + @param[out] Context Calling context from the hardware, will be updated with the current power button status. +**/ +VOID +EFIAPI +PowerButtonGetContext ( + IN DATABASE_RECORD *Record, + OUT PCH_SMM_CONTEXT *Context + ); + +/** + Check whether Power Button status of two contexts match + + @param[in] Context1 Context 1 that includes Power Button status 1 + @param[in] Context2 Context 2 that includes Power Button status 2 + + @retval FALSE Power Button status match + @retval TRUE Power Button status don't match +**/ +BOOLEAN +EFIAPI +PowerButtonCmpContext ( + IN PCH_SMM_CONTEXT *Context1, + IN PCH_SMM_CONTEXT *Context2 + ); + +/** + This function is responsible for calculating and enabling any timers that are required + to dispatch messages to children. The SrcDesc argument isn't acutally used. + + @param[in] SrcDesc Pointer to the PCH_SMM_SOURCE_DESC instance. +**/ +VOID +EFIAPI +PchSmmPeriodicTimerClearSource ( + IN CONST PCH_SMM_SOURCE_DESC *SrcDesc + ); + +/** + This services returns the next SMI tick period that is supported by the chipset. + The order returned is from longest to shortest interval period. + + @param[in] This Pointer to the EFI_SMM_PERIODIC_TIMER_DISPATCH2_PROTOCOL instance. + @param[in, out] SmiTickInterval Pointer to pointer of the next shorter SMI interval period that is supported by the child. + + @retval EFI_SUCCESS The service returned successfully. + @retval EFI_INVALID_PARAMETER The parameter SmiTickInterval is invalid. +**/ +EFI_STATUS +PchSmmPeriodicTimerDispatchGetNextShorterInterval ( + IN CONST EFI_SMM_PERIODIC_TIMER_DISPATCH2_PROTOCOL *This, + IN OUT UINT64 **SmiTickInterval + ); + +/** + Install PCH SMM periodic timer control protocol + + @param[in] Handle handle for this driver + + @retval EFI_SUCCESS Driver initialization completed successfully +**/ +EFI_STATUS +EFIAPI +InstallPchSmmPeriodicTimerControlProtocol ( + IN EFI_HANDLE Handle + ); + +/** + Install protocols of PCH specifics SMI types, including + PCH TCO SMI types, PCH PCIE SMI types, PCH ACPI SMI types, PCH MISC SMI types. + + @retval the result of protocol installation +**/ +EFI_STATUS +InstallPchSmiDispatchProtocols ( + VOID + ); + +/** + The function to dispatch all callback function of PCH SMI types. + + @retval EFI_SUCCESS Function successfully completed + @retval EFI_UNSUPPORTED no +**/ +EFI_STATUS +PchSmiTypeCallbackDispatcher ( + IN DATABASE_RECORD *Record + ); + +/** + The register function used to register SMI handler of IoTrap event. + This is internal function and only used by Iotrap module. + + @param[in] DispatchFunction Pointer to dispatch function to be invoked for this SMI source + @param[in] IoTrapIndex Index number of IOTRAP register + @param[out] DispatchHandle Handle of dispatch function to register. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source description + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database record + @retval EFI_SUCCESS The database record is created successfully. +**/ +EFI_STATUS +PchInternalIoTrapSmiRegister ( + IN PCH_SMI_DISPATCH_CALLBACK DispatchFunction, + IN UINTN IoTrapIndex, + OUT EFI_HANDLE *DispatchHandle + ); + +/** + Unregister a child SMI source dispatch function with a parent SMM driver + + @param[in] DispatchHandle Handle of dispatch function to deregister. + + @retval EFI_SUCCESS The dispatch function has been successfully + unregistered and the SMI source has been disabled + if there are no other registered child dispatch + functions for this SMI source. + @retval EFI_INVALID_PARAMETER Handle is invalid. +**/ +EFI_STATUS +PchInternalIoTrapSmiUnRegister ( + IN EFI_HANDLE DispatchHandle + ); + +/** + Register an eSPI SMI handler based on the type + + @param[in] DispatchFunction Callback in an event of eSPI SMI + @param[in] PchSmiTypes The eSPI type published by PchSmiDispatch + @param[out] DispatchHandle The callback handle + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source description + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database record + @retval EFI_SUCCESS Registration is successful. +**/ +EFI_STATUS +PchInternalEspiSmiRegister ( + IN PCH_SMI_DISPATCH_CALLBACK DispatchFunction, + IN PCH_SMI_TYPES PchSmiTypes, + OUT EFI_HANDLE *DispatchHandle + ); + +/** + Unregister an eSPI SMI handler + + @param[in] DispatchHandle Handle of dispatch function to deregister. + + @retval EFI_SUCCESS The dispatch function has been successfully + unregistered and the SMI source has been disabled + if there are no other registered child dispatch + functions for this SMI source. + @retval EFI_INVALID_PARAMETER Handle is invalid. +**/ +EFI_STATUS +PchInternalEspiSmiUnRegister ( + IN EFI_HANDLE DispatchHandle + ); + +/** + The internal function used to create and insert a database record + for SMI record of Pch Smi types. + + @param[in] SrcDesc The pointer to the SMI source description + @param[in] DispatchFunction Pointer to dispatch function to be invoked for this SMI source + @param[in] PchSmiType Specific SMI type of PCH SMI + @param[out] DispatchHandle Handle of dispatch function to register. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source description + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database record + @retval EFI_SUCCESS The database record is created successfully. +**/ +EFI_STATUS +PchSmiRecordInsert ( + IN CONST PCH_SMM_SOURCE_DESC *SrcDesc, + IN PCH_SMI_CALLBACK_FUNCTIONS DispatchFunction, + IN PCH_SMI_TYPES PchSmiType, + OUT EFI_HANDLE *DispatchHandle + ); + +extern CONST PCH_SMM_SOURCE_DESC mSrcDescSerialIrq; +extern PCH_SMM_SOURCE_DESC mSrcDescLpcBiosWp; + +/** + Clear the TCO SMI status bit and block after the SMI handling is done + + @param[in] SrcDesc Pointer to the PCH SMI source description table + +**/ +VOID +EFIAPI +PchTcoSmiClearSourceAndBlock ( + CONST PCH_SMM_SOURCE_DESC *SrcDesc + ); + +/** + Clear the TCO SMI status bit after the SMI handling is done + + @param[in] SrcDesc Pointer to the PCH SMI source description table + +**/ +VOID +EFIAPI +PchTcoSmiClearSource ( + CONST PCH_SMM_SOURCE_DESC *SrcDesc + ); + +/** + Initialize Source descriptor structure + + @param[in] SrcDesc Pointer to the PCH SMI source description table +**/ +VOID +EFIAPI +NullInitSourceDesc ( + PCH_SMM_SOURCE_DESC *SrcDesc + ); + +/** + The register function used to register SMI handler of GPI SMI event. + + @param[in] This Pointer to the EFI_SMM_GPI_DISPATCH2_PROTOCOL instance. + @param[in] DispatchFunction Function to register for handler when the specified GPI causes an SMI. + @param[in] RegisterContext Pointer to the dispatch function's context. + The caller fills this context in before calling + the register function to indicate to the register + function the GPI(s) for which the dispatch function + should be invoked. + @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_ACCESS_DENIED Register is not allowed + @retval EFI_INVALID_PARAMETER RegisterContext is invalid. The GPI input value + is not within valid range. + @retval EFI_OUT_OF_RESOURCES There is not enough memory (system or SMM) to manage this child. +**/ +EFI_STATUS +EFIAPI +PchGpiSmiRegister ( + IN CONST EFI_SMM_GPI_DISPATCH2_PROTOCOL *This, + IN EFI_SMM_HANDLER_ENTRY_POINT2 DispatchFunction, + IN EFI_SMM_GPI_REGISTER_CONTEXT *RegisterContext, + OUT EFI_HANDLE *DispatchHandle + ); + +/** + Unregister a GPI SMI source dispatch function with a parent SMM driver + + @param[in] This Pointer to the EFI_SMM_GPI_DISPATCH2_PROTOCOL instance. + @param[in] DispatchHandle Handle of dispatch function to deregister. + + @retval EFI_SUCCESS The dispatch function has been successfully + unregistered and the SMI source has been disabled + if there are no other registered child dispatch + functions for this SMI source. + @retval EFI_INVALID_PARAMETER Handle is invalid. +**/ +EFI_STATUS +EFIAPI +PchGpiSmiUnRegister ( + IN CONST EFI_SMM_GPI_DISPATCH2_PROTOCOL *This, + IN EFI_HANDLE DispatchHandle + ); + +#endif diff --git a/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmCore.c b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmCore.c new file mode 100644 index 0000000000..cb889b5ce3 --- /dev/null +++ b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmCore.c @@ -0,0 +1,905 @@ +/** @file + This driver is responsible for the registration of child drivers + and the abstraction of the PCH SMI sources. + + Copyright (c) 2022, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ +#include "PchSmm.h" +#include "PchSmmHelpers.h" +#include "PchSmiHelper.h" +#include +#include +#include +#include +#include + +#define PROGRESS_CODE_S3_SUSPEND_START PcdGet32 (PcdProgressCodeS3SuspendStart) +// +// MODULE / GLOBAL DATA +// +// Module variables used by the both the main dispatcher and the source dispatchers +// Declared in PchSmm.h +// +GLOBAL_REMOVE_IF_UNREFERENCED UINT16 mAcpiBaseAddr; +GLOBAL_REMOVE_IF_UNREFERENCED UINT16 mTcoBaseAddr; +GLOBAL_REMOVE_IF_UNREFERENCED BOOLEAN mReadyToLock; +GLOBAL_REMOVE_IF_UNREFERENCED BOOLEAN mS3SusStart; +GLOBAL_REMOVE_IF_UNREFERENCED UINT32 mNumOfRootPorts; + +GLOBAL_REMOVE_IF_UNREFERENCED PRIVATE_DATA mPrivateData = { + { + NULL, + NULL + }, // CallbackDataBase linked list head + NULL, // EFI handle returned when calling InstallMultipleProtocolInterfaces + NULL, // + { // protocol arrays + // + // elements within the array + // + { + PROTOCOL_SIGNATURE, + UsbType, + &gEfiSmmUsbDispatch2ProtocolGuid, + {{ + (PCH_SMM_GENERIC_REGISTER) PchPiSmmCoreRegister, + (PCH_SMM_GENERIC_UNREGISTER) PchPiSmmCoreUnRegister + }} + }, + { + PROTOCOL_SIGNATURE, + SxType, + &gEfiSmmSxDispatch2ProtocolGuid, + {{ + (PCH_SMM_GENERIC_REGISTER) PchPiSmmCoreRegister, + (PCH_SMM_GENERIC_UNREGISTER) PchPiSmmCoreUnRegister + }} + }, + { + PROTOCOL_SIGNATURE, + SwType, + &gEfiSmmSwDispatch2ProtocolGuid, + {{ + (PCH_SMM_GENERIC_REGISTER) PchSwSmiRegister, + (PCH_SMM_GENERIC_UNREGISTER) PchSwSmiUnRegister, + (UINTN) MAXIMUM_SWI_VALUE + }} + }, + { + PROTOCOL_SIGNATURE, + GpiType, + &gEfiSmmGpiDispatch2ProtocolGuid, + {{ + (PCH_SMM_GENERIC_REGISTER) PchGpiSmiRegister, + (PCH_SMM_GENERIC_UNREGISTER) PchGpiSmiUnRegister, + (UINTN) PCH_GPIO_NUM_SUPPORTED_GPIS + }} + }, + { + PROTOCOL_SIGNATURE, + PowerButtonType, + &gEfiSmmPowerButtonDispatch2ProtocolGuid, + {{ + (PCH_SMM_GENERIC_REGISTER) PchPiSmmCoreRegister, + (PCH_SMM_GENERIC_UNREGISTER) PchPiSmmCoreUnRegister + }} + }, + { + PROTOCOL_SIGNATURE, + PeriodicTimerType, + &gEfiSmmPeriodicTimerDispatch2ProtocolGuid, + {{ + (PCH_SMM_GENERIC_REGISTER) PchPiSmmCoreRegister, + (PCH_SMM_GENERIC_UNREGISTER) PchPiSmmCoreUnRegister, + (UINTN) PchSmmPeriodicTimerDispatchGetNextShorterInterval + }} + }, + } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED CONTEXT_FUNCTIONS mContextFunctions[PCH_SMM_PROTOCOL_TYPE_MAX] = { + { + NULL, + NULL, + NULL + }, + { + SxGetContext, + SxCmpContext, + NULL + }, + { + NULL, + NULL, + NULL + }, + { + NULL, + NULL, + NULL + }, + { + PowerButtonGetContext, + PowerButtonCmpContext, + NULL + }, + { + PeriodicTimerGetContext, + PeriodicTimerCmpContext, + PeriodicTimerGetCommBuffer + }, +}; + +// +// PROTOTYPES +// +// Functions use only in this file +// +EFI_STATUS +EFIAPI +PchSmmCoreDispatcher ( + IN EFI_HANDLE SmmImageHandle, + IN CONST VOID *PchSmmCore, OPTIONAL + IN OUT VOID *CommunicationBuffer, + IN OUT UINTN *SourceSize + ); + +// +// FUNCTIONS +// +/** + SMM ready to lock notification event handler. + + @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 SmmReadyToLockCallback runs successfully + +**/ +EFI_STATUS +EFIAPI +SmmReadyToLockCallback ( + IN CONST EFI_GUID *Protocol, + IN VOID *Interface, + IN EFI_HANDLE Handle + ) +{ + mReadyToLock = TRUE; + + return EFI_SUCCESS; +} + +/** + PchSmiDispatcher SMM Module Entry Point\n + - Introduction\n + The PchSmiDispatcher module is an SMM driver which provides SMI handler registration + services for PCH generated SMIs. + + - Details\n + This module provides SMI handler registration servicies for PCH SMIs. + NOTE: All the register/unregister functions will be locked after SMM ready to boot signal event. + Please make sure no handler is installed after that. + + - @pre + - EFI_SMM_BASE2_PROTOCOL + - Documented in the System Management Mode Core Interface Specification + - EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL + - Documented in the UEFI 2.0 Specification and above + - EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL + - This is to ensure that PCI MMIO and IO resource has been prepared and available for this driver to allocate. + - EFI_SMM_CPU_PROTOCOL + + - @result + The PchSmiDispatcher driver produces: + - EFI_SMM_USB_DISPATCH2_PROTOCOL + - EFI_SMM_SX_DISPATCH2_PROTOCOL + - EFI_SMM_SW_DISPATCH2_PROTOCOL + - EFI_SMM_GPI_DISPATCH2_PROTOCOL + - EFI_SMM_IO_TRAP_DISPATCH2_PROTOCOL + - EFI_SMM_POWER_BUTTON_DISPATCH2_PROTOCOL + - EFI_SMM_PERIODIC_TIMER_DISPATCH2_PROTOCOL + - @link _PCH_SMM_IO_TRAP_CONTROL_PROTOCOL PCH_SMM_IO_TRAP_CONTROL_PROTOCOL @endlink + - @link _PCH_PCIE_SMI_DISPATCH_PROTOCOL PCH_PCIE_SMI_DISPATCH_PROTOCOL @endlink + - @link _PCH_TCO_SMI_DISPATCH_PROTOCOL PCH_TCO_SMI_DISPATCH_PROTOCOL @endlink + - @link _PCH_ACPI_SMI_DISPATCH_PROTOCOL PCH_ACPI_SMI_DISPATCH_PROTOCOL @endlink + - @link _PCH_SMI_DISPATCH_PROTOCOL PCH_SMI_DISPATCH_PROTOCOL @endlink + + @param[in] ImageHandle Pointer to the loaded image protocol for this driver + @param[in] SystemTable Pointer to the EFI System Table + + @retval EFI_SUCCESS PchSmmDispatcher Initialization completed. +**/ +EFI_STATUS +EFIAPI +InitializePchSmmDispatcher ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + VOID *SmmReadyToLockRegistration; + + mS3SusStart = FALSE; + + PchSmiDispatchUpdateDescriptors (); + + // + // Access ACPI Base Addresses Register + // + mAcpiBaseAddr = PmcGetAcpiBase (); + ASSERT (mAcpiBaseAddr != 0); + + // + // Access TCO Base Addresses Register + // + PchTcoBaseGet (&mTcoBaseAddr); + ASSERT (mTcoBaseAddr != 0); + + mNumOfRootPorts = GetPchMaxPciePortNum (); + + // + // Register a callback function to handle subsequent SMIs. This callback + // will be called by SmmCoreDispatcher. + // + Status = gSmst->SmiHandlerRegister (PchSmmCoreDispatcher, NULL, &mPrivateData.SmiHandle); + ASSERT_EFI_ERROR (Status); + // + // Initialize Callback DataBase + // + InitializeListHead (&mPrivateData.CallbackDataBase); + + // + // Enable SMIs on the PCH now that we have a callback + // + PchSmmInitHardware (); + + // + // Install and initialize all the needed protocols + // + PchSwDispatchInit (); + PchSmmPublishDispatchProtocols (); + InstallPchSmiDispatchProtocols (); + InstallPchSmmPeriodicTimerControlProtocol (mPrivateData.InstallMultProtHandle); + + // + // Register EFI_SMM_READY_TO_LOCK_PROTOCOL_GUID notify function. + // + Status = gSmst->SmmRegisterProtocolNotify ( + &gEfiSmmReadyToLockProtocolGuid, + SmmReadyToLockCallback, + &SmmReadyToLockRegistration + ); + ASSERT_EFI_ERROR (Status); + + return EFI_SUCCESS; +} + +/** + The internal function used to create and insert a database record + + @param[in] InsertRecord Record to insert to database. + @param[out] DispatchHandle Handle of dispatch function to register. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source description + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database record + @retval EFI_SUCCESS The database record is created successfully. +**/ +EFI_STATUS +SmmCoreInsertRecord ( + IN DATABASE_RECORD *NewRecord, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + DATABASE_RECORD *Record; + + if ((NewRecord == NULL) || + (NewRecord->Signature != DATABASE_RECORD_SIGNATURE)) + { + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + Status = gSmst->SmmAllocatePool (EfiRuntimeServicesData, sizeof (DATABASE_RECORD), (VOID **) &Record); + if (EFI_ERROR (Status)) { + ASSERT (FALSE); + return EFI_OUT_OF_RESOURCES; + } + CopyMem (Record, NewRecord, sizeof (DATABASE_RECORD)); + + // + // After ensuring the source of event is not null, we will insert the record into the database + // + InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link); + + // + // Child's handle will be the address linked list link in the record + // + *DispatchHandle = (EFI_HANDLE) (&Record->Link); + + return EFI_SUCCESS; +} + +/** + Unregister a child SMI source dispatch function with a parent SMM driver + + @param[in] This Protocol instance pointer. + @param[in] DispatchHandle Handle of dispatch function to deregister. + + @retval EFI_SUCCESS The dispatch function has been successfully + unregistered and the SMI source has been disabled + if there are no other registered child dispatch + functions for this SMI source. + @retval EFI_INVALID_PARAMETER Handle is invalid. + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock event has been triggered +**/ +EFI_STATUS +EFIAPI +PchPiSmmCoreUnRegister ( + IN PCH_SMM_GENERIC_PROTOCOL *This, + IN EFI_HANDLE *DispatchHandle + ) +{ + DATABASE_RECORD *RecordToDelete; + EFI_STATUS Status; + PCH_SMM_QUALIFIED_PROTOCOL *Qualified; + + Qualified = QUALIFIED_PROTOCOL_FROM_GENERIC (This); + RecordToDelete = DATABASE_RECORD_FROM_LINK (DispatchHandle); + Status = PchSmmCoreUnRegister (NULL, DispatchHandle); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileUnregisterHandler ( + Qualified->Guid, + RecordToDelete->Callback, + &RecordToDelete->ChildContext, + RecordToDelete->ContextSize + ); + } + return Status; +} + +/** + Register a child SMI dispatch function with a parent SMM driver. + + @param[in] This Pointer to the PCH_SMM_GENERIC_PROTOCOL instance. + @param[in] DispatchFunction Pointer to dispatch function to be invoked for this SMI source. + @param[in] DispatchContext Pointer to the dispatch function's context. + @param[out] DispatchHandle Handle of dispatch function, for when interfacing + with the parent SMM driver, will be the address of linked + list link in the call back record. + + @retval EFI_OUT_OF_RESOURCES Insufficient resources to create database record + @retval EFI_INVALID_PARAMETER The input parameter is invalid + @retval EFI_SUCCESS The dispatch function has been successfully + registered and the SMI source has been enabled. +**/ +EFI_STATUS +EFIAPI +PchPiSmmCoreRegister ( + IN PCH_SMM_GENERIC_PROTOCOL *This, + IN EFI_SMM_HANDLER_ENTRY_POINT2 DispatchFunction, + IN PCH_SMM_CONTEXT *DispatchContext, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + DATABASE_RECORD Record; + PCH_SMM_QUALIFIED_PROTOCOL *Qualified; + PCH_SMM_SOURCE_DESC NullSourceDesc; + + // + // Initialize NullSourceDesc + // + NullInitSourceDesc (&NullSourceDesc); + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock == TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the EndOfDxe event has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + ZeroMem (&Record, sizeof (DATABASE_RECORD)); + + // + // Gather information about the registration request + // + Record.Callback = DispatchFunction; + + Qualified = QUALIFIED_PROTOCOL_FROM_GENERIC (This); + + Record.ProtocolType = Qualified->Type; + + Record.ContextFunctions = mContextFunctions[Qualified->Type]; + // + // Perform linked list housekeeping + // + Record.Signature = DATABASE_RECORD_SIGNATURE; + + switch (Qualified->Type) { + // + // By the end of this switch statement, we'll know the + // source description the child is registering for + // + case UsbType: + Record.ContextSize = sizeof (EFI_SMM_USB_REGISTER_CONTEXT); + CopyMem (&Record.ChildContext, DispatchContext, Record.ContextSize); + // + // Check the validity of Context Type + // + if ((Record.ChildContext.Usb.Type < UsbLegacy) || (Record.ChildContext.Usb.Type > UsbWake)) { + return EFI_INVALID_PARAMETER; + } + + PchSmmUsbUpdateDescriptors (); + MapUsbToSrcDesc (DispatchContext, &Record.SrcDesc); + Record.ClearSource = NULL; + // + // use default clear source function + // + break; + + case SxType: + Record.ContextSize = sizeof (EFI_SMM_SX_REGISTER_CONTEXT); + CopyMem (&Record.ChildContext, DispatchContext, Record.ContextSize); + // + // Check the validity of Context Type and Phase + // + if ((Record.ChildContext.Sx.Type < SxS0) || + (Record.ChildContext.Sx.Type >= EfiMaximumSleepType) || + (Record.ChildContext.Sx.Phase < SxEntry) || + (Record.ChildContext.Sx.Phase >= EfiMaximumPhase) + ) { + return EFI_INVALID_PARAMETER; + } + + CopyMem (&Record.SrcDesc, &mSxSourceDesc, sizeof (PCH_SMM_SOURCE_DESC)); + Record.ClearSource = NULL; + // + // use default clear source function + // + break; + + case PowerButtonType: + Record.ContextSize = sizeof (EFI_SMM_POWER_BUTTON_REGISTER_CONTEXT); + CopyMem (&Record.ChildContext, DispatchContext, Record.ContextSize); + // + // Check the validity of Context Phase + // + if ((Record.ChildContext.PowerButton.Phase < EfiPowerButtonEntry) || + (Record.ChildContext.PowerButton.Phase > EfiPowerButtonExit)) + { + return EFI_INVALID_PARAMETER; + } + + CopyMem (&Record.SrcDesc, &mPowerButtonSourceDesc, sizeof (PCH_SMM_SOURCE_DESC)); + Record.ClearSource = NULL; + // + // use default clear source function + // + break; + + case PeriodicTimerType: + Record.ContextSize = sizeof (EFI_SMM_PERIODIC_TIMER_REGISTER_CONTEXT); + CopyMem (&Record.ChildContext, DispatchContext, Record.ContextSize); + // + // Check the validity of timer value + // + if (DispatchContext->PeriodicTimer.SmiTickInterval <= 0) { + return EFI_INVALID_PARAMETER; + } + + MapPeriodicTimerToSrcDesc (DispatchContext, &Record.SrcDesc); + Record.MiscData.TimerSmiEnabled = TRUE; + Record.ClearSource = PchSmmPeriodicTimerClearSource; + break; + + default: + return EFI_INVALID_PARAMETER; + break; + } + + if (CompareSources (&Record.SrcDesc, &NullSourceDesc)) { + return EFI_INVALID_PARAMETER; + } + + // + // After ensuring the source of event is not null, we will insert the record into the database + // Child's handle will be the address linked list link in the record + // + Status = SmmCoreInsertRecord ( + &Record, + DispatchHandle + ); + ASSERT_EFI_ERROR (Status); + + if (Record.ClearSource == NULL) { + // + // Clear the SMI associated w/ the source using the default function + // + PchSmmClearSource (&Record.SrcDesc); + } else { + // + // This source requires special handling to clear + // + Record.ClearSource (&Record.SrcDesc); + } + + PchSmmEnableSource (&Record.SrcDesc); + SmiHandlerProfileRegisterHandler ( + Qualified->Guid, + DispatchFunction, + (UINTN)RETURN_ADDRESS (0), + DispatchContext, + Record.ContextSize + ); + + return EFI_SUCCESS; +} + +/** + Unregister a child SMI source dispatch function with a parent SMM driver. + + @param[in] This Pointer to the PCH_SMM_GENERIC_PROTOCOL instance. + @param[in] DispatchHandle Handle of dispatch function to deregister. + + @retval EFI_SUCCESS The dispatch function has been successfully + unregistered and the SMI source has been disabled + if there are no other registered child dispatch + functions for this SMI source. + @retval EFI_INVALID_PARAMETER Handle is invalid. +**/ +EFI_STATUS +EFIAPI +PchSmmCoreUnRegister ( + IN PCH_SMM_GENERIC_PROTOCOL *This, + IN EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + BOOLEAN NeedClearEnable; + UINTN DescIndex; + DATABASE_RECORD *RecordToDelete; + DATABASE_RECORD *RecordInDb; + LIST_ENTRY *LinkInDb; + + if (DispatchHandle == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock == TRUE) { + DEBUG ((DEBUG_ERROR, "UnRegister is not allowed if the SmmReadyToLock event has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + RecordToDelete = DATABASE_RECORD_FROM_LINK (DispatchHandle); + + // + // Take the entry out of the linked list + // + if (RecordToDelete->Link.ForwardLink == (LIST_ENTRY *) EFI_BAD_POINTER) { + return EFI_INVALID_PARAMETER; + } + + RemoveEntryList (&RecordToDelete->Link); + + // + // Loop through all the souces in record linked list to see if any source enable is equal. + // If any source enable is equal, we do not want to disable it. + // + for (DescIndex = 0; DescIndex < NUM_EN_BITS; ++DescIndex) { + if (IS_BIT_DESC_NULL (RecordToDelete->SrcDesc.En[DescIndex])) { + continue; + } + NeedClearEnable = TRUE; + LinkInDb = GetFirstNode (&mPrivateData.CallbackDataBase); + while (!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) { + RecordInDb = DATABASE_RECORD_FROM_LINK (LinkInDb); + if (IsBitEqualToAnySourceEn (&RecordToDelete->SrcDesc.En[DescIndex], &RecordInDb->SrcDesc)) { + NeedClearEnable = FALSE; + break; + } + LinkInDb = GetNextNode (&mPrivateData.CallbackDataBase, &RecordInDb->Link); + } + if (NeedClearEnable == FALSE) { + continue; + } + WriteBitDesc (&RecordToDelete->SrcDesc.En[DescIndex], 0, FALSE); + } + Status = gSmst->SmmFreePool (RecordToDelete); + if (EFI_ERROR (Status)) { + ASSERT (FALSE); + return Status; + } + return EFI_SUCCESS; +} + +/** + This function clears the pending SMI status before set EOS. + NOTE: This only clears the pending SMI with known reason. + Please do not clear unknown pending SMI status since that will hide potential issues. + + @param[in] SmiStsValue SMI status + @param[in] SciEn Sci Enable status +**/ +STATIC +VOID +ClearPendingSmiStatus ( + UINT32 SmiStsValue, + BOOLEAN SciEn + ) +{ + // + // Clear NewCentury status if it's not handled. + // + if (SmiStsValue & B_ACPI_IO_SMI_STS_TCO) { + if (IoRead16 (mTcoBaseAddr + R_TCO_IO_TCO1_STS) & B_TCO_IO_TCO1_STS_NEWCENTURY) { + PchTcoSmiClearSourceAndBlock (&mSrcDescNewCentury); + } + } + // Clear PWRBTNOR_STS if it's not handled. + // + if (IoRead16 (mAcpiBaseAddr + R_ACPI_IO_PM1_STS) & B_ACPI_IO_PM1_STS_PRBTNOR) { + IoWrite16 (mAcpiBaseAddr + R_ACPI_IO_PM1_STS, B_ACPI_IO_PM1_STS_PRBTNOR); + } + // + // Clear WADT_STS if this is triggered by WADT timer. + // + if (!SciEn) { + if ((IoRead32 (mAcpiBaseAddr + R_ACPI_IO_GPE0_EN_127_96) & B_ACPI_IO_GPE0_EN_127_96_WADT) && + (IoRead32 (mAcpiBaseAddr + R_ACPI_IO_GPE0_STS_127_96) & B_ACPI_IO_GPE0_STS_127_96_WADT)) { + IoWrite32 (mAcpiBaseAddr + R_ACPI_IO_GPE0_STS_127_96, B_ACPI_IO_GPE0_STS_127_96_WADT); + } + } + // + // Clear GPIO_UNLOCK_SMI_STS in case it is set as GPIO Unlock SMI is not supported + // + if (SmiStsValue & B_ACPI_IO_SMI_STS_GPIO_UNLOCK) { + IoWrite32 (mAcpiBaseAddr + R_ACPI_IO_SMI_STS, B_ACPI_IO_SMI_STS_GPIO_UNLOCK); + } +} + +/** + The callback function to handle subsequent SMIs. This callback will be called by SmmCoreDispatcher. + + @param[in] SmmImageHandle Not used + @param[in] PchSmmCore Not used + @param[in, out] CommunicationBuffer Not used + @param[in, out] SourceSize Not used + + @retval EFI_SUCCESS Function successfully completed +**/ +EFI_STATUS +EFIAPI +PchSmmCoreDispatcher ( + IN EFI_HANDLE SmmImageHandle, + IN CONST VOID *PchSmmCore, + IN OUT VOID *CommunicationBuffer, + IN OUT UINTN *SourceSize + ) +{ + // + // Used to prevent infinite loops + // + UINTN EscapeCount; + + BOOLEAN ContextsMatch; + BOOLEAN EosSet; + BOOLEAN SxChildWasDispatched; + + DATABASE_RECORD *RecordInDb; + LIST_ENTRY *LinkInDb; + DATABASE_RECORD *RecordToExhaust; + LIST_ENTRY *LinkToExhaust; + + PCH_SMM_CONTEXT Context; + VOID *CommBuffer; + UINTN CommBufferSize; + + EFI_STATUS Status; + BOOLEAN SciEn; + UINT32 SmiEnValue; + UINT32 SmiStsValue; + UINT8 Port74Save; + UINT8 Port76Save; + + PCH_SMM_SOURCE_DESC ActiveSource; + + // + // Initialize ActiveSource + // + NullInitSourceDesc (&ActiveSource); + + EscapeCount = 3; + ContextsMatch = FALSE; + EosSet = FALSE; + SxChildWasDispatched = FALSE; + Status = EFI_SUCCESS; + + // + // Save IO index registers + // @note: Save/Restore port 70h directly might break NMI_EN# setting, + // then save/restore 74h/76h instead. + // @note: CF8 is not saved. Prefer method is to use MMIO instead of CF8 + // + Port76Save = IoRead8 (R_RTC_IO_EXT_INDEX_ALT); + Port74Save = IoRead8 (R_RTC_IO_INDEX_ALT); + + if (!IsListEmpty (&mPrivateData.CallbackDataBase)) { + // + // We have children registered w/ us -- continue + // + while ((!EosSet) && (EscapeCount > 0)) { + EscapeCount--; + + LinkInDb = GetFirstNode (&mPrivateData.CallbackDataBase); + + // + // Cache SciEn, SmiEnValue and SmiStsValue to determine if source is active + // + SciEn = PchSmmGetSciEn (); + SmiEnValue = IoRead32 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_SMI_EN)); + SmiStsValue = IoRead32 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_SMI_STS)); + + while (!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) { + RecordInDb = DATABASE_RECORD_FROM_LINK (LinkInDb); + + // + // look for the first active source + // + if (!SourceIsActive (&RecordInDb->SrcDesc, SciEn, SmiEnValue, SmiStsValue)) { + // + // Didn't find the source yet, keep looking + // + LinkInDb = GetNextNode (&mPrivateData.CallbackDataBase, &RecordInDb->Link); + + // + // if it's the last one, try to clear EOS + // + if (IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) { + // + // Clear pending SMI status before EOS + // + ClearPendingSmiStatus (SmiStsValue, SciEn); + EosSet = PchSmmSetAndCheckEos (); + } + } else { + // + // We found a source. If this is a sleep type, we have to go to + // appropriate sleep state anyway.No matter there is sleep child or not + // + if (RecordInDb->ProtocolType == SxType) { + SxChildWasDispatched = TRUE; + } + // + // "cache" the source description and don't query I/O anymore + // + CopyMem ((VOID *) &ActiveSource, (VOID *) &(RecordInDb->SrcDesc), sizeof (PCH_SMM_SOURCE_DESC)); + LinkToExhaust = LinkInDb; + + // + // exhaust the rest of the queue looking for the same source + // + while (!IsNull (&mPrivateData.CallbackDataBase, LinkToExhaust)) { + RecordToExhaust = DATABASE_RECORD_FROM_LINK (LinkToExhaust); + // + // RecordToExhaust->Link might be removed (unregistered) by Callback function, and then the + // system will hang in ASSERT() while calling GetNextNode(). + // To prevent the issue, we need to get next record in DB here (before Callback function). + // + LinkToExhaust = GetNextNode (&mPrivateData.CallbackDataBase, &RecordToExhaust->Link); + + if (CompareSources (&RecordToExhaust->SrcDesc, &ActiveSource)) { + // + // These source descriptions are equal, so this callback should be + // dispatched. + // + if (RecordToExhaust->ContextFunctions.GetContext != NULL) { + // + // This child requires that we get a calling context from + // hardware and compare that context to the one supplied + // by the child. + // + ASSERT (RecordToExhaust->ContextFunctions.CmpContext != NULL); + + // + // Make sure contexts match before dispatching event to child + // + RecordToExhaust->ContextFunctions.GetContext (RecordToExhaust, &Context); + ContextsMatch = RecordToExhaust->ContextFunctions.CmpContext (&Context, &RecordToExhaust->ChildContext); + + } else { + // + // This child doesn't require any more calling context beyond what + // it supplied in registration. Simply pass back what it gave us. + // + Context = RecordToExhaust->ChildContext; + ContextsMatch = TRUE; + } + + if (ContextsMatch) { + if (RecordToExhaust->ProtocolType == PchSmiDispatchType) { + // + // For PCH SMI dispatch protocols + // + PchSmiTypeCallbackDispatcher (RecordToExhaust); + } else { + if ((RecordToExhaust->ProtocolType == SxType) && (Context.Sx.Type == SxS3) && (Context.Sx.Phase == SxEntry) && !mS3SusStart) { + REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PROGRESS_CODE_S3_SUSPEND_START); + mS3SusStart = TRUE; + } + // + // For EFI standard SMI dispatch protocols + // + if (RecordToExhaust->Callback != NULL) { + if (RecordToExhaust->ContextFunctions.GetCommBuffer != NULL) { + // + // This callback function needs CommBuffer and CommBufferSize. + // Get those from child and then pass to callback function. + // + RecordToExhaust->ContextFunctions.GetCommBuffer (RecordToExhaust, &CommBuffer, &CommBufferSize); + } else { + // + // Child doesn't support the CommBuffer and CommBufferSize. + // Just pass NULL value to callback function. + // + CommBuffer = NULL; + CommBufferSize = 0; + } + + PERF_START_EX (NULL, "SmmFunction", NULL, AsmReadTsc (), RecordToExhaust->ProtocolType); + RecordToExhaust->Callback ((EFI_HANDLE) & RecordToExhaust->Link, &Context, CommBuffer, &CommBufferSize); + PERF_END_EX (NULL, "SmmFunction", NULL, AsmReadTsc (), RecordToExhaust->ProtocolType); + if (RecordToExhaust->ProtocolType == SxType) { + SxChildWasDispatched = TRUE; + } + } else { + ASSERT (FALSE); + } + } + } + } + } + + if (RecordInDb->ClearSource == NULL) { + // + // Clear the SMI associated w/ the source using the default function + // + PchSmmClearSource (&ActiveSource); + } else { + // + // This source requires special handling to clear + // + RecordInDb->ClearSource (&ActiveSource); + } + // + // Clear pending SMI status before EOS + // + ClearPendingSmiStatus (SmiStsValue, SciEn); + // + // Also, try to clear EOS + // + EosSet = PchSmmSetAndCheckEos (); + // + // Queue is empty, reset the search + // + break; + } + } + } + } + // + // Restore IO index registers + // @note: Save/Restore port 70h directly might break NMI_EN# setting, + // then save/restore 74h/76h instead. + // + IoWrite8 (R_RTC_IO_EXT_INDEX_ALT, Port76Save); + IoWrite8 (R_RTC_IO_INDEX_ALT, Port74Save); + + return Status; +} diff --git a/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmGpi.c b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmGpi.c new file mode 100644 index 0000000000..4c59c07bac --- /dev/null +++ b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmGpi.c @@ -0,0 +1,255 @@ +/** @file + File to contain all the hardware specific stuff for the Smm Gpi dispatch protocol. + + Copyright (c) 2022, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ +#include "PchSmm.h" +#include "PchSmmHelpers.h" +#include +#include +#include + +// +// Structure for GPI SMI is a template which needs to have +// GPI Smi bit offset and Smi Status & Enable registers updated (accordingly +// to choosen group and pad number) after adding it to SMM Callback database +// + +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mPchGpiSourceDescTemplate = { + PCH_SMM_NO_FLAGS, + { + NULL_BIT_DESC_INITIALIZER, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + GPIO_ADDR_TYPE, {0x0} + }, + S_GPIO_PCR_GP_SMI_STS, 0x0, + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_GPIO_SMI + } +}; + +/** + The register function used to register SMI handler of GPI SMI event. + + @param[in] This Pointer to the EFI_SMM_GPI_DISPATCH2_PROTOCOL instance. + @param[in] DispatchFunction Function to register for handler when the specified GPI causes an SMI. + @param[in] RegisterContext Pointer to the dispatch function's context. + The caller fills this context in before calling + the register function to indicate to the register + function the GPI(s) for which the dispatch function + should be invoked. + @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_ACCESS_DENIED Register is not allowed + @retval EFI_INVALID_PARAMETER RegisterContext is invalid. The GPI input value + is not within valid range. + @retval EFI_OUT_OF_RESOURCES There is not enough memory (system or SMM) to manage this child. +**/ +EFI_STATUS +EFIAPI +PchGpiSmiRegister ( + IN CONST EFI_SMM_GPI_DISPATCH2_PROTOCOL *This, + IN EFI_SMM_HANDLER_ENTRY_POINT2 DispatchFunction, + IN EFI_SMM_GPI_REGISTER_CONTEXT *RegisterContext, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + DATABASE_RECORD Record; + GPIO_PAD GpioPad; + UINT8 GpiSmiBitOffset; + UINT32 GpiHostSwOwnRegAddress; + UINT32 GpiSmiStsRegAddress; + UINT32 Data32And; + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock == TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the EndOfDxe event has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status = GpioGetPadAndSmiRegs ( + (UINT32) RegisterContext->GpiNum, + &GpioPad, + &GpiSmiBitOffset, + &GpiHostSwOwnRegAddress, + &GpiSmiStsRegAddress + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + ZeroMem (&Record, sizeof (DATABASE_RECORD)); + + // + // Gather information about the registration request + // + Record.Callback = DispatchFunction; + Record.ChildContext.Gpi = *RegisterContext; + Record.ProtocolType = GpiType; + Record.Signature = DATABASE_RECORD_SIGNATURE; + + CopyMem (&Record.SrcDesc, &mPchGpiSourceDescTemplate, sizeof (PCH_SMM_SOURCE_DESC) ); + + Record.SrcDesc.Sts[0].Reg.Data.raw = GpiSmiStsRegAddress; // GPI SMI Status register + Record.SrcDesc.Sts[0].Bit = GpiSmiBitOffset; // Bit position for selected pad + + // + // Insert GpiSmi handler to PchSmmCore database + // + *DispatchHandle = NULL; + + Status = SmmCoreInsertRecord ( + &Record, + DispatchHandle + ); + ASSERT_EFI_ERROR (Status); + + SmiHandlerProfileRegisterHandler ( + &gEfiSmmGpiDispatch2ProtocolGuid, + (EFI_SMM_HANDLER_ENTRY_POINT2) DispatchFunction, + (UINTN)RETURN_ADDRESS (0), + RegisterContext, + sizeof(*RegisterContext) + ); + + // + // Enable GPI SMI + // HOSTSW_OWN with respect to generating GPI SMI has negative logic: + // - 0 (ACPI mode) - GPIO pad will be capable of generating SMI/NMI/SCI + // - 1 (GPIO mode) - GPIO pad will not generate SMI/NMI/SCI + // + Data32And = (UINT32)~(1u << GpiSmiBitOffset); + MmioAnd32 (GpiHostSwOwnRegAddress, Data32And); + + return EFI_SUCCESS; +} + +/** + Unregister a GPI SMI source dispatch function with a parent SMM driver + + @param[in] This Pointer to the EFI_SMM_GPI_DISPATCH2_PROTOCOL instance. + @param[in] DispatchHandle Handle of dispatch function to deregister. + + @retval EFI_SUCCESS The dispatch function has been successfully + unregistered and the SMI source has been disabled + if there are no other registered child dispatch + functions for this SMI source. + @retval EFI_INVALID_PARAMETER Handle is invalid. +**/ +EFI_STATUS +EFIAPI +PchGpiSmiUnRegister ( + IN CONST EFI_SMM_GPI_DISPATCH2_PROTOCOL *This, + IN EFI_HANDLE DispatchHandle + ) +{ + EFI_STATUS Status; + DATABASE_RECORD *RecordToDelete; + DATABASE_RECORD *RecordInDb; + LIST_ENTRY *LinkInDb; + GPIO_PAD GpioPad; + UINT8 GpiSmiBitOffset; + UINT32 GpiHostSwOwnRegAddress; + UINT32 GpiSmiStsRegAddress; + UINT32 Data32Or; + UINT32 Data32And; + BOOLEAN DisableGpiSmiSource; + + + if (DispatchHandle == NULL) { + return EFI_INVALID_PARAMETER; + } + + RecordToDelete = DATABASE_RECORD_FROM_LINK (DispatchHandle); + if ((RecordToDelete->Signature != DATABASE_RECORD_SIGNATURE) || + (RecordToDelete->ProtocolType != GpiType)) { + return EFI_INVALID_PARAMETER; + } + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock == TRUE) { + DEBUG ((DEBUG_ERROR, "UnRegister is not allowed if the SmmReadyToLock event has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + DisableGpiSmiSource = TRUE; + // + // Loop through all sources in record linked list to see if any other GPI SMI + // is installed on the same pin. If no then disable GPI SMI capability on this pad + // + LinkInDb = GetFirstNode (&mPrivateData.CallbackDataBase); + while (!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) { + RecordInDb = DATABASE_RECORD_FROM_LINK (LinkInDb); + LinkInDb = GetNextNode (&mPrivateData.CallbackDataBase, &RecordInDb->Link); + // + // If this is the record to delete skip it + // + if (RecordInDb == RecordToDelete) { + continue; + } + // + // Check if record is GPI SMI type + // + if (RecordInDb->ProtocolType == GpiType) { + // + // Check if same GPIO pad is the source of this SMI + // + if (RecordInDb->ChildContext.Gpi.GpiNum == RecordToDelete->ChildContext.Gpi.GpiNum) { + DisableGpiSmiSource = FALSE; + break; + } + } + } + + if (DisableGpiSmiSource) { + GpioGetPadAndSmiRegs ( + (UINT32) RecordToDelete->ChildContext.Gpi.GpiNum, + &GpioPad, + &GpiSmiBitOffset, + &GpiHostSwOwnRegAddress, + &GpiSmiStsRegAddress + ); + + Data32Or = 1u << GpiSmiBitOffset; + Data32And = 0xFFFFFFFF; + MmioOr32 (GpiHostSwOwnRegAddress, Data32Or); + } + + + RemoveEntryList (&RecordToDelete->Link); + ZeroMem (RecordToDelete, sizeof (DATABASE_RECORD)); + Status = gSmst->SmmFreePool (RecordToDelete); + + if (EFI_ERROR (Status)) { + ASSERT (FALSE); + return Status; + } + SmiHandlerProfileUnregisterHandler ( + &gEfiSmmGpiDispatch2ProtocolGuid, + RecordToDelete->Callback, + &RecordToDelete->ChildContext, + RecordToDelete->ContextSize + ); + return EFI_SUCCESS; +} diff --git a/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmHelpers.c b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmHelpers.c new file mode 100644 index 0000000000..724a383855 --- /dev/null +++ b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmHelpers.c @@ -0,0 +1,332 @@ +/** @file + Helper functions for PCH SMM dispatcher. + + Copyright (c) 2022, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ +#include "PchSmmHelpers.h" +#include + +/// +/// #define BIT_ZERO 0x00000001 +/// +GLOBAL_REMOVE_IF_UNREFERENCED CONST UINT32 BIT_ZERO = 0x00000001; + +/// +/// SUPPORT / HELPER FUNCTIONS (PCH version-independent) +/// + +/** + Compare 2 SMM source descriptors' enable settings. + + @param[in] Src1 Pointer to the PCH SMI source description table 1 + @param[in] Src2 Pointer to the PCH SMI source description table 2 + + @retval TRUE The enable settings of the 2 SMM source descriptors are identical. + @retval FALSE The enable settings of the 2 SMM source descriptors are not identical. +**/ +BOOLEAN +CompareEnables ( + CONST IN PCH_SMM_SOURCE_DESC *Src1, + CONST IN PCH_SMM_SOURCE_DESC *Src2 + ) +{ + BOOLEAN IsEqual; + UINTN DescIndex; + + IsEqual = TRUE; + for (DescIndex = 0; DescIndex < NUM_EN_BITS; DescIndex++) { + /// + /// It's okay to compare a NULL bit description to a non-NULL bit description. + /// They are unequal and these tests will generate the correct result. + /// + if (Src1->En[DescIndex].Bit != Src2->En[DescIndex].Bit || + Src1->En[DescIndex].Reg.Type != Src2->En[DescIndex].Reg.Type || + Src1->En[DescIndex].Reg.Data.raw != Src2->En[DescIndex].Reg.Data.raw + ) { + IsEqual = FALSE; + break; + /// + /// out of for loop + /// + } + } + + return IsEqual; +} + +/** + Compare a bit descriptor to the enables of source descriptor. Includes null address type. + + @param[in] BitDesc Pointer to the PCH SMI bit descriptor + @param[in] Src Pointer to the PCH SMI source description table 2 + + @retval TRUE The bit desc is equal to any of the enables in source descriptor + @retval FALSE The bid desc is not equal to all of the enables in source descriptor +**/ +BOOLEAN +IsBitEqualToAnySourceEn ( + CONST IN PCH_SMM_BIT_DESC *BitDesc, + CONST IN PCH_SMM_SOURCE_DESC *Src + ) +{ + BOOLEAN IsEqual; + UINTN DescIndex; + + IsEqual = FALSE; + + for (DescIndex = 0; DescIndex < NUM_EN_BITS; ++DescIndex) { + if ((BitDesc->Reg.Type == Src->En[DescIndex].Reg.Type) && + (BitDesc->Reg.Data.raw == Src->En[DescIndex].Reg.Data.raw) && + (BitDesc->Bit == Src->En[DescIndex].Bit)) { + IsEqual = TRUE; + break; + } + } + return IsEqual; +} + +/** + Compare 2 SMM source descriptors' statuses. + + @param[in] Src1 Pointer to the PCH SMI source description table 1 + @param[in] Src2 Pointer to the PCH SMI source description table 2 + + @retval TRUE The statuses of the 2 SMM source descriptors are identical. + @retval FALSE The statuses of the 2 SMM source descriptors are not identical. +**/ +BOOLEAN +CompareStatuses ( + CONST IN PCH_SMM_SOURCE_DESC *Src1, + CONST IN PCH_SMM_SOURCE_DESC *Src2 + ) +{ + BOOLEAN IsEqual; + UINTN DescIndex; + + IsEqual = TRUE; + + for (DescIndex = 0; DescIndex < NUM_STS_BITS; DescIndex++) { + /// + /// It's okay to compare a NULL bit description to a non-NULL bit description. + /// They are unequal and these tests will generate the correct result. + /// + if (Src1->Sts[DescIndex].Bit != Src2->Sts[DescIndex].Bit || + Src1->Sts[DescIndex].Reg.Type != Src2->Sts[DescIndex].Reg.Type || + Src1->Sts[DescIndex].Reg.Data.raw != Src2->Sts[DescIndex].Reg.Data.raw + ) { + IsEqual = FALSE; + break; + /// + /// out of for loop + /// + } + } + + return IsEqual; +} + +/** + Compare 2 SMM source descriptors, based on Enable settings and Status settings of them. + + @param[in] Src1 Pointer to the PCH SMI source description table 1 + @param[in] Src2 Pointer to the PCH SMI source description table 2 + + @retval TRUE The 2 SMM source descriptors are identical. + @retval FALSE The 2 SMM source descriptors are not identical. +**/ +BOOLEAN +CompareSources ( + CONST IN PCH_SMM_SOURCE_DESC *Src1, + CONST IN PCH_SMM_SOURCE_DESC *Src2 + ) +{ + return (BOOLEAN) (CompareEnables (Src1, Src2) && CompareStatuses (Src1, Src2)); +} + +/** + Check if an SMM source is active. + + @param[in] Src Pointer to the PCH SMI source description table + @param[in] SciEn Indicate if SCI is enabled or not + @param[in] SmiEnValue Value from R_ACPI_IO_SMI_EN + @param[in] SmiStsValue Value from R_ACPI_IO_SMI_STS + + @retval TRUE It is active. + @retval FALSE It is inactive. +**/ +BOOLEAN +SourceIsActive ( + CONST IN PCH_SMM_SOURCE_DESC *Src, + CONST IN BOOLEAN SciEn, + CONST IN UINT32 SmiEnValue, + CONST IN UINT32 SmiStsValue + ) +{ + UINTN DescIndex; + + /// + /// This source is dependent on SciEn, and SciEn == 1. An ACPI OS is present, + /// so we shouldn't do anything w/ this source until SciEn == 0. + /// + if ((Src->Flags == PCH_SMM_SCI_EN_DEPENDENT) && (SciEn)) { + return FALSE; + } + + /// + /// Checking top level SMI status. If the status is not active, return false immediately + /// + if (!IS_BIT_DESC_NULL (Src->PmcSmiSts)) { + if ((Src->PmcSmiSts.Reg.Type == ACPI_ADDR_TYPE) && + (Src->PmcSmiSts.Reg.Data.acpi == R_ACPI_IO_SMI_STS) && + ((SmiStsValue & (1u << Src->PmcSmiSts.Bit)) == 0)) { + return FALSE; + } + } + + /// + /// Read each bit desc from hardware and make sure it's a one + /// + for (DescIndex = 0; DescIndex < NUM_EN_BITS; DescIndex++) { + if (!IS_BIT_DESC_NULL (Src->En[DescIndex])) { + if ((Src->En[DescIndex].Reg.Type == ACPI_ADDR_TYPE) && + (Src->En[DescIndex].Reg.Data.acpi == R_ACPI_IO_SMI_EN) && + ((SmiEnValue & (1u << Src->En[DescIndex].Bit)) == 0)) { + return FALSE; + } else if (ReadBitDesc (&Src->En[DescIndex]) == 0) { + return FALSE; + } + } + } + + /// + /// Read each bit desc from hardware and make sure it's a one + /// + for (DescIndex = 0; DescIndex < NUM_STS_BITS; DescIndex++) { + if (!IS_BIT_DESC_NULL (Src->Sts[DescIndex])) { + if ((Src->Sts[DescIndex].Reg.Type == ACPI_ADDR_TYPE) && + (Src->Sts[DescIndex].Reg.Data.acpi == R_ACPI_IO_SMI_STS) && + ((SmiStsValue & (1u << Src->Sts[DescIndex].Bit)) == 0)) { + return FALSE; + } else if (ReadBitDesc (&Src->Sts[DescIndex]) == 0) { + return FALSE; + } + } + } + + return TRUE; +} + +/** + Enable the SMI source event by set the SMI enable bit, this function would also clear SMI + status bit to make initial state is correct + + @param[in] SrcDesc Pointer to the PCH SMI source description table + +**/ +VOID +PchSmmEnableSource ( + CONST PCH_SMM_SOURCE_DESC *SrcDesc + ) +{ + UINTN DescIndex; + + /// + /// Set enables to 1 by writing a 1 + /// + for (DescIndex = 0; DescIndex < NUM_EN_BITS; DescIndex++) { + if (!IS_BIT_DESC_NULL (SrcDesc->En[DescIndex])) { + WriteBitDesc (&SrcDesc->En[DescIndex], 1, FALSE); + } + } + /// + /// Clear statuses to 0 by writing a 1 + /// + for (DescIndex = 0; DescIndex < NUM_STS_BITS; DescIndex++) { + if (!IS_BIT_DESC_NULL (SrcDesc->Sts[DescIndex])) { + WriteBitDesc (&SrcDesc->Sts[DescIndex], 1, TRUE); + } + } +} + +/** + Disable the SMI source event by clear the SMI enable bit + + @param[in] SrcDesc Pointer to the PCH SMI source description table + +**/ +VOID +PchSmmDisableSource ( + CONST PCH_SMM_SOURCE_DESC *SrcDesc + ) +{ + UINTN DescIndex; + + for (DescIndex = 0; DescIndex < NUM_EN_BITS; DescIndex++) { + if (!IS_BIT_DESC_NULL (SrcDesc->En[DescIndex])) { + WriteBitDesc (&SrcDesc->En[DescIndex], 0, FALSE); + } + } +} + +/** + Clear the SMI status bit by set the source bit of SMI status register + + @param[in] SrcDesc Pointer to the PCH SMI source description table + +**/ +VOID +PchSmmClearSource ( + CONST PCH_SMM_SOURCE_DESC *SrcDesc + ) +{ + UINTN DescIndex; + + for (DescIndex = 0; DescIndex < NUM_STS_BITS; DescIndex++) { + if (!IS_BIT_DESC_NULL (SrcDesc->Sts[DescIndex])) { + WriteBitDesc (&SrcDesc->Sts[DescIndex], 1, TRUE); + } + } +} + +/** + Sets the source to a 1 and then waits for it to clear. + Be very careful when calling this function -- it will not + ASSERT. An acceptable case to call the function is when + waiting for the NEWCENTURY_STS bit to clear (which takes + 3 RTCCLKs). + + @param[in] SrcDesc Pointer to the PCH SMI source description table + +**/ +VOID +PchSmmClearSourceAndBlock ( + CONST PCH_SMM_SOURCE_DESC *SrcDesc + ) +{ + UINTN DescIndex; + BOOLEAN IsSet; + + for (DescIndex = 0; DescIndex < NUM_STS_BITS; DescIndex++) { + + if (!IS_BIT_DESC_NULL (SrcDesc->Sts[DescIndex])) { + /// + /// Write the bit + /// + WriteBitDesc (&SrcDesc->Sts[DescIndex], 1, TRUE); + + /// + /// Don't return until the bit actually clears. + /// + IsSet = TRUE; + while (IsSet) { + IsSet = ReadBitDesc (&SrcDesc->Sts[DescIndex]); + /// + /// IsSet will eventually clear -- or else we'll have + /// an infinite loop. + /// + } + } + } +} + diff --git a/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmHelpers.h b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmHelpers.h new file mode 100644 index 0000000000..93ab8564ff --- /dev/null +++ b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmHelpers.h @@ -0,0 +1,163 @@ +/** @file + Helper functions for PCH SMM + + Copyright (c) 2022, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ +#ifndef PCH_SMM_HELPERS_H +#define PCH_SMM_HELPERS_H + +#include "PchSmm.h" +#include "PchxSmmHelpers.h" +// +// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// SUPPORT / HELPER FUNCTIONS (PCH version-independent) +// + +/** + Publish SMI Dispatch protocols. + + +**/ +VOID +PchSmmPublishDispatchProtocols ( + VOID + ); + +/** + Compare 2 SMM source descriptors' enable settings. + + @param[in] Src1 Pointer to the PCH SMI source description table 1 + @param[in] Src2 Pointer to the PCH SMI source description table 2 + + @retval TRUE The enable settings of the 2 SMM source descriptors are identical. + @retval FALSE The enable settings of the 2 SMM source descriptors are not identical. +**/ +BOOLEAN +CompareEnables ( + CONST IN PCH_SMM_SOURCE_DESC *Src1, + CONST IN PCH_SMM_SOURCE_DESC *Src2 + ); + +/** + Compare a bit descriptor to the enables of source descriptor. Includes null address type. + + @param[in] BitDesc Pointer to the PCH SMI bit descriptor + @param[in] Src Pointer to the PCH SMI source description table 2 + + @retval TRUE The bit desc is equal to any of the enables in source descriptor + @retval FALSE The bid desc is not equal to all of the enables in source descriptor +**/ +BOOLEAN +IsBitEqualToAnySourceEn ( + CONST IN PCH_SMM_BIT_DESC *BitDesc, + CONST IN PCH_SMM_SOURCE_DESC *Src + ); + +/** + Compare 2 SMM source descriptors' statuses. + + @param[in] Src1 Pointer to the PCH SMI source description table 1 + @param[in] Src2 Pointer to the PCH SMI source description table 2 + + @retval TRUE The statuses of the 2 SMM source descriptors are identical. + @retval FALSE The statuses of the 2 SMM source descriptors are not identical. +**/ +BOOLEAN +CompareStatuses ( + CONST IN PCH_SMM_SOURCE_DESC *Src1, + CONST IN PCH_SMM_SOURCE_DESC *Src2 + ); + +/** + Compare 2 SMM source descriptors, based on Enable settings and Status settings of them. + + @param[in] Src1 Pointer to the PCH SMI source description table 1 + @param[in] Src2 Pointer to the PCH SMI source description table 2 + + @retval TRUE The 2 SMM source descriptors are identical. + @retval FALSE The 2 SMM source descriptors are not identical. +**/ +BOOLEAN +CompareSources ( + CONST IN PCH_SMM_SOURCE_DESC *Src1, + CONST IN PCH_SMM_SOURCE_DESC *Src2 + ); + +/** + Check if an SMM source is active. + + @param[in] Src Pointer to the PCH SMI source description table + @param[in] SciEn Indicate if SCI is enabled or not + @param[in] SmiEnValue Value from R_PCH_SMI_EN + @param[in] SmiStsValue Value from R_PCH_SMI_STS + + @retval TRUE It is active. + @retval FALSE It is inactive. +**/ +BOOLEAN +SourceIsActive ( + CONST IN PCH_SMM_SOURCE_DESC *Src, + CONST IN BOOLEAN SciEn, + CONST IN UINT32 SmiEnValue, + CONST IN UINT32 SmiStsValue + ); + +/** + Enable the SMI source event by set the SMI enable bit, this function would also clear SMI + status bit to make initial state is correct + + @param[in] SrcDesc Pointer to the PCH SMI source description table + +**/ +VOID +PchSmmEnableSource ( + CONST PCH_SMM_SOURCE_DESC *SrcDesc + ); + +/** + Disable the SMI source event by clear the SMI enable bit + + @param[in] SrcDesc Pointer to the PCH SMI source description table + +**/ +VOID +PchSmmDisableSource ( + CONST PCH_SMM_SOURCE_DESC *SrcDesc + ); + +/** + Clear the SMI status bit by set the source bit of SMI status register + + @param[in] SrcDesc Pointer to the PCH SMI source description table + +**/ +VOID +PchSmmClearSource ( + CONST PCH_SMM_SOURCE_DESC *SrcDesc + ); + +/** + Sets the source to a 1 and then waits for it to clear. + Be very careful when calling this function -- it will not + ASSERT. An acceptable case to call the function is when + waiting for the NEWCENTURY_STS bit to clear (which takes + 3 RTCCLKs). + + @param[in] SrcDesc Pointer to the PCH SMI source description table + +**/ +VOID +PchSmmClearSourceAndBlock ( + CONST PCH_SMM_SOURCE_DESC *SrcDesc + ); + +/** + Performs update of SmmUsb descriptors with values that have to be evaluated during runtime. +**/ +VOID +PchSmmUsbUpdateDescriptors ( + VOID + ); + +#endif diff --git a/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmPeriodicTimer.c b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmPeriodicTimer.c new file mode 100644 index 0000000000..3078d0c696 --- /dev/null +++ b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmPeriodicTimer.c @@ -0,0 +1,670 @@ +/** @file + File to contain all the hardware specific stuff for the Periodical Timer dispatch protocol. + + Copyright (c) 2022, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ +#include "PchSmmHelpers.h" +#include +#include + +// +// There is only one instance for PeriodicTimerCommBuffer. +// It's safe in SMM since there is no re-entry for the function. +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_SMM_PERIODIC_TIMER_CONTEXT mPchPeriodicTimerCommBuffer; + +typedef enum { + PERIODIC_TIMER= 0, + SWSMI_TIMER, + NUM_TIMERS +} SUPPORTED_TIMER; + +typedef struct _TIMER_INTERVAL { + UINT64 Interval; + UINT8 AssociatedTimer; +} TIMER_INTERVAL; + +#define NUM_INTERVALS 8 + +// +// Time constants, in 100 nano-second units +// +#define TIME_64s 640000000 ///< 64 s +#define TIME_32s 320000000 ///< 32 s +#define TIME_16s 160000000 ///< 16 s +#define TIME_8s 80000000 ///< 8 s +#define TIME_64ms 640000 ///< 64 ms +#define TIME_32ms 320000 ///< 32 ms +#define TIME_16ms 160000 ///< 16 ms +#define TIME_1_5ms 15000 ///< 1.5 ms + +typedef enum { + INDEX_TIME_64s = 0, + INDEX_TIME_32s, + INDEX_TIME_16s, + INDEX_TIME_8s, + INDEX_TIME_64ms, + INDEX_TIME_32ms, + INDEX_TIME_16ms, + INDEX_TIME_1_5ms, + INDEX_TIME_MAX +} TIMER_INTERVAL_INDEX; + +static TIMER_INTERVAL mSmmPeriodicTimerIntervals[NUM_INTERVALS] = { + { + TIME_64s, + PERIODIC_TIMER + }, + { + TIME_32s, + PERIODIC_TIMER + }, + { + TIME_16s, + PERIODIC_TIMER + }, + { + TIME_8s, + PERIODIC_TIMER + }, + { + TIME_64ms, + SWSMI_TIMER + }, + { + TIME_32ms, + SWSMI_TIMER + }, + { + TIME_16ms, + SWSMI_TIMER + }, + { + TIME_1_5ms, + SWSMI_TIMER + }, +}; + +typedef struct _TIMER_INFO { + UINTN NumChildren; ///< number of children using this timer + UINT64 MinReqInterval; ///< minimum interval required by children + UINTN CurrentSetting; ///< interval this timer is set at right now (index into interval table) +} TIMER_INFO; + +GLOBAL_REMOVE_IF_UNREFERENCED TIMER_INFO mTimers[NUM_TIMERS]; + +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mTimerSourceDesc[NUM_TIMERS] = { + { + PCH_SMM_NO_FLAGS, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_EN} + }, + S_ACPI_IO_SMI_EN, + N_ACPI_IO_SMI_EN_PERIODIC + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_PERIODIC + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_PERIODIC + } + }, + { + PCH_SMM_NO_FLAGS, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_EN} + }, + S_ACPI_IO_SMI_EN, + N_ACPI_IO_SMI_EN_SWSMI_TMR + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_SWSMI_TMR + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_SWSMI_TMR + } + } +}; + +/** + Program Smm Periodic Timer + + @param[in] SrcDesc Pointer to the PCH_SMM_SOURCE_DESC instance. +**/ +VOID +PchSmmPeriodicTimerProgramTimers ( + IN CONST PCH_SMM_SOURCE_DESC *SrcDesc + ); + +/** + Convert the dispatch context to the timer interval, this function will assert if then either: + (1) The context contains an invalid interval + (2) The timer interval table is corrupt + + @param[in] DispatchContext The pointer to the Dispatch Context + + @retval TIMER_INTERVAL The timer interval of input dispatch context +**/ +TIMER_INTERVAL * +ContextToTimerInterval ( + IN PCH_SMM_CONTEXT *DispatchContext + ) +{ + UINTN loopvar; + + /// + /// Determine which timer this child is using + /// + for (loopvar = 0; loopvar < NUM_INTERVALS; loopvar++) { + if (((DispatchContext->PeriodicTimer.SmiTickInterval == 0) && + (DispatchContext->PeriodicTimer.Period >= mSmmPeriodicTimerIntervals[loopvar].Interval)) || + (DispatchContext->PeriodicTimer.SmiTickInterval == mSmmPeriodicTimerIntervals[loopvar].Interval)) { + return &mSmmPeriodicTimerIntervals[loopvar]; + } + } + /// + /// If this assertion fires, then either: + /// (1) the context contains an invalid interval + /// (2) the timer interval table is corrupt + /// + ASSERT (FALSE); + + return NULL; +} + +/** + Figure out which timer the child is requesting and + send back the source description + + @param[in] DispatchContext The pointer to the Dispatch Context instances + @param[out] SrcDesc The pointer to the source description + +**/ +VOID +MapPeriodicTimerToSrcDesc ( + IN PCH_SMM_CONTEXT *DispatchContext, + OUT PCH_SMM_SOURCE_DESC *SrcDesc + ) +{ + TIMER_INTERVAL *TimerInterval; + + /// + /// Figure out which timer the child is requesting and + /// send back the source description + /// + TimerInterval = ContextToTimerInterval (DispatchContext); + if (TimerInterval == NULL) { + return; + } + + CopyMem ( + (VOID *) SrcDesc, + (VOID *) (&mTimerSourceDesc[TimerInterval->AssociatedTimer]), + sizeof (PCH_SMM_SOURCE_DESC) + ); + + /// + /// Program the value of the interval into hardware + /// + PchSmmPeriodicTimerProgramTimers (SrcDesc); +} + +/** + Update the elapsed time from the Interval data of DATABASE_RECORD + + @param[in] Record The pointer to the DATABASE_RECORD. + @param[out] HwContext The Context to be updated. + +**/ +VOID +EFIAPI +PeriodicTimerGetContext ( + IN DATABASE_RECORD *Record, + OUT PCH_SMM_CONTEXT *HwContext + ) +{ + TIMER_INTERVAL *TimerInterval; + + ASSERT (Record->ProtocolType == PeriodicTimerType); + + TimerInterval = ContextToTimerInterval (&Record->ChildContext); + if (TimerInterval == NULL) { + return; + } + /// + /// Ignore the hardware context. It's not required for this protocol. + /// Instead, just increment the child's context. + /// Update the elapsed time w/ the data from our tables + /// + Record->MiscData.ElapsedTime += mTimers[TimerInterval->AssociatedTimer].MinReqInterval; + *HwContext = Record->ChildContext; +} + +/** + Check whether Periodic Timer of two contexts match + + @param[in] Context1 Context 1 that includes Periodic Timer 1 + @param[in] Context2 Context 2 that includes Periodic Timer 2 + + @retval FALSE Periodic Timer match + @retval TRUE Periodic Timer don't match +**/ +BOOLEAN +EFIAPI +PeriodicTimerCmpContext ( + IN PCH_SMM_CONTEXT *HwContext, + IN PCH_SMM_CONTEXT *ChildContext + ) +{ + DATABASE_RECORD *Record; + Record = DATABASE_RECORD_FROM_CHILDCONTEXT (ChildContext); + + if (!Record->MiscData.TimerSmiEnabled) { + return FALSE; + } + if (Record->MiscData.ElapsedTime >= ChildContext->PeriodicTimer.Period) { + /// + /// For EDKII, the ElapsedTime is reset when PeriodicTimerGetCommBuffer + /// + return TRUE; + } else { + return FALSE; + } +} + +/** + Gather the CommBuffer information of SmmPeriodicTimerDispatch2. + + @param[in] Record No use + @param[out] CommBuffer Point to the CommBuffer structure + @param[out] CommBufferSize Point to the Size of CommBuffer structure + +**/ +VOID +EFIAPI +PeriodicTimerGetCommBuffer ( + IN DATABASE_RECORD *Record, + OUT VOID **CommBuffer, + OUT UINTN *CommBufferSize + ) +{ + ASSERT (Record->ProtocolType == PeriodicTimerType); + + mPchPeriodicTimerCommBuffer.ElapsedTime = Record->MiscData.ElapsedTime; + + /// + /// For EDKII, the ElapsedTime is reset here + /// + Record->MiscData.ElapsedTime = 0; + + /// + /// Return the CommBuffer + /// + *CommBuffer = (VOID *) &mPchPeriodicTimerCommBuffer; + *CommBufferSize = sizeof (EFI_SMM_PERIODIC_TIMER_CONTEXT); +} + +/** + Program Smm Periodic Timer + + @param[in] SrcDesc Pointer to the PCH_SMM_SOURCE_DESC instance. +**/ +VOID +PchSmmPeriodicTimerProgramTimers ( + IN CONST PCH_SMM_SOURCE_DESC *SrcDesc + ) +{ + SUPPORTED_TIMER Timer; + DATABASE_RECORD *RecordInDb; + LIST_ENTRY *LinkInDb; + TIMER_INTERVAL *TimerInterval; + + /// + /// Find the minimum required interval for each timer + /// + for (Timer = 0; Timer < NUM_TIMERS; Timer++) { + mTimers[Timer].MinReqInterval = ~ (UINT64) 0x0; + mTimers[Timer].NumChildren = 0; + } + + LinkInDb = GetFirstNode (&mPrivateData.CallbackDataBase); + while (!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) { + RecordInDb = DATABASE_RECORD_FROM_LINK (LinkInDb); + if (RecordInDb->ProtocolType == PeriodicTimerType) { + if (RecordInDb->MiscData.TimerSmiEnabled) { + /// + /// This child is registerd with the PeriodicTimer protocol + /// + TimerInterval = ContextToTimerInterval (&RecordInDb->ChildContext); + if (TimerInterval == NULL) { + return; + } + + Timer = TimerInterval->AssociatedTimer; + if (Timer < 0 || Timer >= NUM_TIMERS) { + ASSERT (FALSE); + CpuDeadLoop (); + return; + } + if (mTimers[Timer].MinReqInterval > RecordInDb->ChildContext.PeriodicTimer.SmiTickInterval) { + mTimers[Timer].MinReqInterval = RecordInDb->ChildContext.PeriodicTimer.SmiTickInterval; + } + mTimers[Timer].NumChildren++; + } + } + LinkInDb = GetNextNode (&mPrivateData.CallbackDataBase, &RecordInDb->Link); + } + /// + /// Program the hardware + /// + if (mTimers[PERIODIC_TIMER].NumChildren > 0) { + switch (mTimers[PERIODIC_TIMER].MinReqInterval) { + case TIME_64s: + PmcSetPeriodicSmiRate (PmcPeriodicSmiRate64s); + mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_64s; + break; + + case TIME_32s: + PmcSetPeriodicSmiRate (PmcPeriodicSmiRate32s); + mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_32s; + break; + + case TIME_16s: + PmcSetPeriodicSmiRate (PmcPeriodicSmiRate16s); + mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_16s; + break; + + case TIME_8s: + PmcSetPeriodicSmiRate (PmcPeriodicSmiRate8s); + mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_8s; + break; + + default: + ASSERT (FALSE); + break; + } + + /// + /// Restart the timer here, just need to clear the SMI + /// + if (SrcDesc->Sts[0].Bit == N_ACPI_IO_SMI_STS_PERIODIC) { + PchSmmClearSource (&mTimerSourceDesc[PERIODIC_TIMER]); + } + } else { + PchSmmDisableSource (&mTimerSourceDesc[PERIODIC_TIMER]); + } + + if (mTimers[SWSMI_TIMER].NumChildren > 0) { + switch (mTimers[SWSMI_TIMER].MinReqInterval) { + case TIME_64ms: + PmcSetSwSmiRate (PmcSwSmiRate64ms); + mTimers[SWSMI_TIMER].CurrentSetting = INDEX_TIME_64ms; + break; + + case TIME_32ms: + PmcSetSwSmiRate (PmcSwSmiRate32ms); + mTimers[SWSMI_TIMER].CurrentSetting = INDEX_TIME_32ms; + break; + + case TIME_16ms: + PmcSetSwSmiRate (PmcSwSmiRate16ms); + mTimers[SWSMI_TIMER].CurrentSetting = INDEX_TIME_16ms; + break; + + case TIME_1_5ms: + PmcSetSwSmiRate (PmcSwSmiRate1p5ms); + mTimers[SWSMI_TIMER].CurrentSetting = INDEX_TIME_1_5ms; + break; + + default: + ASSERT (FALSE); + break; + } + + /// + /// Restart the timer here, need to disable, clear, then enable to restart this timer + /// + if (SrcDesc->Sts[0].Bit == N_ACPI_IO_SMI_STS_SWSMI_TMR) { + PchSmmDisableSource (&mTimerSourceDesc[SWSMI_TIMER]); + PchSmmClearSource (&mTimerSourceDesc[SWSMI_TIMER]); + PchSmmEnableSource (&mTimerSourceDesc[SWSMI_TIMER]); + } + } else { + PchSmmDisableSource (&mTimerSourceDesc[SWSMI_TIMER]); + } +} + +/** + This services returns the next SMI tick period that is supported by the chipset. + The order returned is from longest to shortest interval period. + + @param[in] This Pointer to the EFI_SMM_PERIODIC_TIMER_DISPATCH2_PROTOCOL instance. + @param[in, out] SmiTickInterval Pointer to pointer of the next shorter SMI interval period that is supported by the child. + + @retval EFI_SUCCESS The service returned successfully. + @retval EFI_INVALID_PARAMETER The parameter SmiTickInterval is invalid. +**/ +EFI_STATUS +PchSmmPeriodicTimerDispatchGetNextShorterInterval ( + IN CONST EFI_SMM_PERIODIC_TIMER_DISPATCH2_PROTOCOL *This, + IN OUT UINT64 **SmiTickInterval + ) +{ + TIMER_INTERVAL *IntervalPointer; + + ASSERT (SmiTickInterval != NULL); + if (SmiTickInterval == NULL) { + return EFI_INVALID_PARAMETER; + } + + IntervalPointer = (TIMER_INTERVAL *) *SmiTickInterval; + + if (IntervalPointer == NULL) { + /// + /// The first time child requesting an interval + /// + IntervalPointer = &mSmmPeriodicTimerIntervals[0]; + } else if (IntervalPointer == &mSmmPeriodicTimerIntervals[NUM_INTERVALS - 1]) { + /// + /// At end of the list + /// + IntervalPointer = NULL; + } else { + if ((IntervalPointer >= &mSmmPeriodicTimerIntervals[0]) && + (IntervalPointer < &mSmmPeriodicTimerIntervals[NUM_INTERVALS - 1]) + ) { + /// + /// Get the next interval in the list + /// + IntervalPointer++; + } else { + /// + /// Input is out of range + /// + return EFI_INVALID_PARAMETER; + } + } + + if (IntervalPointer != NULL) { + *SmiTickInterval = &IntervalPointer->Interval; + } else { + *SmiTickInterval = NULL; + } + + return EFI_SUCCESS; +} + +/** + This function is responsible for calculating and enabling any timers that are required + to dispatch messages to children. The SrcDesc argument isn't acutally used. + + @param[in] SrcDesc Pointer to the PCH_SMM_SOURCE_DESC instance. + +**/ +VOID +EFIAPI +PchSmmPeriodicTimerClearSource ( + IN CONST PCH_SMM_SOURCE_DESC *SrcDesc + ) +{ + PchSmmPeriodicTimerProgramTimers (SrcDesc); +} + + +/** + Check if the handle is in type of PeriodicTimer + + @retval TRUE The handle is in type of PeriodicTimer. + @retval FALSE The handle is not in type of PeriodicTimer. +**/ +BOOLEAN +IsSmmPeriodicTimerHandle ( + IN EFI_HANDLE DispatchHandle + ) +{ + DATABASE_RECORD *RecordInDb; + LIST_ENTRY *LinkInDb; + + LinkInDb = GetFirstNode (&mPrivateData.CallbackDataBase); + while (!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) { + if (DispatchHandle == (EFI_HANDLE) LinkInDb) { + RecordInDb = DATABASE_RECORD_FROM_LINK (LinkInDb); + if (RecordInDb->ProtocolType == PeriodicTimerType) { + return TRUE; + } + } + LinkInDb = GetNextNode (&mPrivateData.CallbackDataBase, LinkInDb); + } + return FALSE; +} + +/** + Pause SMM periodic timer callback function. + + This function disable the SMI enable of SMI timer according to the DispatchHandle, + which is returned by SMM periodic timer callback registration. + + @retval EFI_SUCCESS This operation is complete. + @retval EFI_INVALID_PARAMETER The DispatchHandle is invalid. +**/ +EFI_STATUS +EFIAPI +PchSmmPeriodicTimerControlPause ( + IN PCH_SMM_PERIODIC_TIMER_CONTROL_PROTOCOL *This, + IN EFI_HANDLE DispatchHandle + ) +{ + DATABASE_RECORD *RecordInDb; + + if (IsSmmPeriodicTimerHandle (DispatchHandle) == FALSE) { + return EFI_INVALID_PARAMETER; + } + + RecordInDb = DATABASE_RECORD_FROM_LINK (DispatchHandle); + RecordInDb->MiscData.TimerSmiEnabled = FALSE; + // + // reset the timer interval per SMI trigger due to stop a periodic timer SMI + // + PchSmmPeriodicTimerProgramTimers (&RecordInDb->SrcDesc); + return EFI_SUCCESS; +} + +/** + Resume SMM periodic timer callback function. + + This function enable the SMI enable of SMI timer according to the DispatchHandle, + which is returned by SMM periodic timer callback registration. + + @retval EFI_SUCCESS This operation is complete. + @retval EFI_INVALID_PARAMETER The DispatchHandle is invalid. +**/ +EFI_STATUS +EFIAPI +PchSmmPeriodicTimerControlResume ( + IN PCH_SMM_PERIODIC_TIMER_CONTROL_PROTOCOL *This, + IN EFI_HANDLE DispatchHandle + ) +{ + DATABASE_RECORD *RecordInDb; + + if (IsSmmPeriodicTimerHandle (DispatchHandle) == FALSE) { + return EFI_INVALID_PARAMETER; + } + RecordInDb = DATABASE_RECORD_FROM_LINK (DispatchHandle); + RecordInDb->MiscData.TimerSmiEnabled = TRUE; + // + // reset the timer interval per SMI trigger due to resume a periodic timer SMI + // + PchSmmPeriodicTimerProgramTimers (&RecordInDb->SrcDesc); + return EFI_SUCCESS; +} + +GLOBAL_REMOVE_IF_UNREFERENCED PCH_SMM_PERIODIC_TIMER_CONTROL_PROTOCOL mPchSmmPeriodicTimerControlProtocol = { + PchSmmPeriodicTimerControlPause, + PchSmmPeriodicTimerControlResume +}; + +/** + Install PCH SMM periodic timer control protocol + + @param[in] Handle handle for this driver + + @retval EFI_SUCCESS Driver initialization completed successfully +**/ +EFI_STATUS +EFIAPI +InstallPchSmmPeriodicTimerControlProtocol ( + IN EFI_HANDLE Handle + ) +{ + EFI_STATUS Status; + + // + // Install protocol interface + // + Status = gSmst->SmmInstallProtocolInterface ( + &Handle, + &gPchSmmPeriodicTimerControlGuid, + EFI_NATIVE_INTERFACE, + &mPchSmmPeriodicTimerControlProtocol + ); + ASSERT_EFI_ERROR (Status); + + return EFI_SUCCESS; +} + diff --git a/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmPowerButton.c b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmPowerButton.c new file mode 100644 index 0000000000..e6d3bf012f --- /dev/null +++ b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmPowerButton.c @@ -0,0 +1,77 @@ +/** @file + File to contain all the hardware specific stuff for the Smm Power Button dispatch protocol. + + Copyright (c) 2022, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ +#include +#include + +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mPowerButtonSourceDesc = { + PCH_SMM_SCI_EN_DEPENDENT, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_PM1_EN} + }, + S_ACPI_IO_PM1_EN, + N_ACPI_IO_PM1_EN_PWRBTN + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_PM1_STS} + }, + S_ACPI_IO_PM1_STS, + N_ACPI_IO_PM1_STS_PWRBTN + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_PM1_STS_REG + } +}; + +/** + Get the power button status. + + @param[in] Record The pointer to the DATABASE_RECORD. + @param[out] Context Calling context from the hardware, will be updated with the current power button status. + +**/ +VOID +EFIAPI +PowerButtonGetContext ( + IN DATABASE_RECORD *Record, + OUT PCH_SMM_CONTEXT *Context + ) +{ + return; +} + +/** + Check whether Power Button status of two contexts match + + @param[in] Context1 Context 1 that includes Power Button status 1 + @param[in] Context2 Context 2 that includes Power Button status 2 + + @retval FALSE Power Button status match + @retval TRUE Power Button status don't match +**/ +BOOLEAN +EFIAPI +PowerButtonCmpContext ( + IN PCH_SMM_CONTEXT *Context1, + IN PCH_SMM_CONTEXT *Context2 + ) +{ + return TRUE; +} diff --git a/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmSw.c b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmSw.c new file mode 100644 index 0000000000..311a21820c --- /dev/null +++ b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmSw.c @@ -0,0 +1,381 @@ +/** @file + File to contain all the hardware specific stuff for the Smm Sw dispatch protocol. + + Copyright (c) 2022, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ +#include "PchSmmHelpers.h" +#include +#include +#include + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_SMM_CPU_PROTOCOL *mSmmCpuProtocol; + +STATIC LIST_ENTRY mSwSmiCallbackDataBase; + +// +// "SWSMI" RECORD +// Linked list data structures +// +#define SW_SMI_RECORD_SIGNATURE SIGNATURE_32 ('S', 'W', 'S', 'M') + +#define SW_SMI_RECORD_FROM_LINK(_record) CR (_record, SW_SMI_RECORD, Link, SW_SMI_RECORD_SIGNATURE) + +typedef struct { + UINT32 Signature; + LIST_ENTRY Link; + EFI_SMM_SW_REGISTER_CONTEXT Context; + EFI_SMM_HANDLER_ENTRY_POINT2 Callback; +} SW_SMI_RECORD; + +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSwSourceDesc = { + PCH_SMM_NO_FLAGS, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_EN} + }, + S_ACPI_IO_SMI_EN, + N_ACPI_IO_SMI_EN_APMC + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_APM + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_APM + } +}; + +/** + Check the SwSmiInputValue to see if there is a duplicated one in the database + + @param[in] SwSmiInputValue SwSmiInputValue + + @retval EFI_SUCCESS There is no duplicated SwSmiInputValue + @retval EFI_INVALID_PARAMETER There is a duplicated SwSmiInputValue +**/ +EFI_STATUS +SmiInputValueDuplicateCheck ( + IN UINTN SwSmiInputValue + ) +{ + SW_SMI_RECORD *SwSmiRecord; + LIST_ENTRY *LinkInDb; + + LinkInDb = GetFirstNode (&mSwSmiCallbackDataBase); + while (!IsNull (&mSwSmiCallbackDataBase, LinkInDb)) { + SwSmiRecord = SW_SMI_RECORD_FROM_LINK (LinkInDb); + if (SwSmiRecord->Context.SwSmiInputValue == SwSmiInputValue) { + return EFI_INVALID_PARAMETER; + } + LinkInDb = GetNextNode (&mSwSmiCallbackDataBase, &SwSmiRecord->Link); + } + + 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 RegisterContext->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] RegisterContext 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 a valid range or is already in use. + @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 +PchSwSmiRegister ( + IN EFI_SMM_SW_DISPATCH2_PROTOCOL *This, + IN EFI_SMM_HANDLER_ENTRY_POINT2 DispatchFunction, + IN EFI_SMM_SW_REGISTER_CONTEXT *DispatchContext, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + SW_SMI_RECORD *SwSmiRecord; + UINTN Index; + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock == TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock event has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + // + // Find available SW SMI value if the input is -1 + // + if (DispatchContext->SwSmiInputValue == (UINTN) -1) { + for (Index = 1; Index < MAXIMUM_SWI_VALUE; Index++) { + if (!EFI_ERROR (SmiInputValueDuplicateCheck (Index))) { + DispatchContext->SwSmiInputValue = Index; + break; + } + } + if (DispatchContext->SwSmiInputValue == (UINTN) -1) { + return EFI_OUT_OF_RESOURCES; + } + } + // + // Check if it's a valid SW SMI value. + // The value must not bigger than 0xFF. + // And the value must not be 0xFF sincie it's used for SmmControll protocol. + // + if (DispatchContext->SwSmiInputValue >= MAXIMUM_SWI_VALUE) { + return EFI_INVALID_PARAMETER; + } + + if (EFI_ERROR (SmiInputValueDuplicateCheck (DispatchContext->SwSmiInputValue))) { + return EFI_INVALID_PARAMETER; + } + + // + // Create database record and add to database + // + Status = gSmst->SmmAllocatePool ( + EfiRuntimeServicesData, + sizeof (SW_SMI_RECORD), + (VOID **) &SwSmiRecord + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to allocate memory for SwSmiRecord! \n")); + return EFI_OUT_OF_RESOURCES; + } + // + // Gather information about the registration request + // + SwSmiRecord->Signature = SW_SMI_RECORD_SIGNATURE; + SwSmiRecord->Context.SwSmiInputValue = DispatchContext->SwSmiInputValue; + SwSmiRecord->Callback = DispatchFunction; + // + // Publish the S/W SMI numbers in Serial logs used for Debug build. + // + DEBUG ((DEBUG_INFO, "SW SMI NUM %x Sw Record at Address 0x%X\n", SwSmiRecord->Context.SwSmiInputValue, SwSmiRecord)); + + InsertTailList (&mSwSmiCallbackDataBase, &SwSmiRecord->Link); + + // + // Child's handle will be the address linked list link in the record + // + *DispatchHandle = (EFI_HANDLE) (&SwSmiRecord->Link); + + return EFI_SUCCESS; +} + +/** + 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 +PchSwSmiUnRegister ( + IN CONST EFI_SMM_SW_DISPATCH2_PROTOCOL *This, + IN EFI_HANDLE DispatchHandle + ) +{ + EFI_STATUS Status; + SW_SMI_RECORD *RecordToDelete; + + if (DispatchHandle == 0) { + return EFI_INVALID_PARAMETER; + } + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock == TRUE) { + DEBUG ((DEBUG_ERROR, "UnRegister is not allowed if the SmmReadyToLock event has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + RecordToDelete = SW_SMI_RECORD_FROM_LINK (DispatchHandle); + // + // Take the entry out of the linked list + // + if (RecordToDelete->Signature != SW_SMI_RECORD_SIGNATURE) { + return EFI_INVALID_PARAMETER; + } + + RemoveEntryList (&RecordToDelete->Link); + ZeroMem (RecordToDelete, sizeof (SW_SMI_RECORD)); + Status = gSmst->SmmFreePool (RecordToDelete); + ASSERT_EFI_ERROR (Status); + + return EFI_SUCCESS; +} + +/** + Main entry point for an SMM handler dispatch or communicate-based callback. + + @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 and quiesced. No other handlers + should still be called. + @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should + still be called. + @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still + be called. + @retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced. +**/ +EFI_STATUS +EFIAPI +PchSwSmiDispatcher ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context, + IN OUT VOID *CommBuffer, + IN OUT UINTN *CommBufferSize + ) +{ + EFI_STATUS Status; + EFI_SMM_SAVE_STATE_IO_INFO SmiIoInfo; + UINTN CpuIndex; + SW_SMI_RECORD *SwSmiRecord; + LIST_ENTRY *LinkInDb; + EFI_SMM_SW_CONTEXT SwSmiCommBuffer; + UINTN SwSmiCommBufferSize; + + SwSmiCommBufferSize = sizeof (EFI_SMM_SW_CONTEXT); + // + // The value in DataPort might not be accurate in multiple thread environment. + // There might be racing condition for R_PCH_IO_APM_STS port. + // Therefor, this is just for reference. + // + SwSmiCommBuffer.DataPort = IoRead8 (R_PCH_IO_APM_STS); + + for (CpuIndex = 0; CpuIndex < gSmst->NumberOfCpus; CpuIndex++) { + Status = mSmmCpuProtocol->ReadSaveState ( + mSmmCpuProtocol, + sizeof (EFI_SMM_SAVE_STATE_IO_INFO), + EFI_SMM_SAVE_STATE_REGISTER_IO, + CpuIndex, + &SmiIoInfo + ); + // + // If this is not the SMI source, skip it. + // + if (EFI_ERROR (Status)) { + continue; + } + // + // If the IO address is not "BYTE" "WRITE" to "R_PCH_IO_APM_CNT (0xB2)", skip it. + // + if ((SmiIoInfo.IoPort != R_PCH_IO_APM_CNT) || + (SmiIoInfo.IoType != EFI_SMM_SAVE_STATE_IO_TYPE_OUTPUT) || + (SmiIoInfo.IoWidth != EFI_SMM_SAVE_STATE_IO_WIDTH_UINT8)) + { + continue; + } + // + // If the IO data is used for SmmControl protocol, skip it. + // + if (SmiIoInfo.IoData == 0xFF) { + continue; + } + + SwSmiCommBuffer.SwSmiCpuIndex = CpuIndex; + SwSmiCommBuffer.CommandPort = (UINT8) SmiIoInfo.IoData; + + LinkInDb = GetFirstNode (&mSwSmiCallbackDataBase); + while (!IsNull (&mSwSmiCallbackDataBase, LinkInDb)) { + SwSmiRecord = SW_SMI_RECORD_FROM_LINK (LinkInDb); + if (SwSmiRecord->Context.SwSmiInputValue == SmiIoInfo.IoData) { + SwSmiRecord->Callback ((EFI_HANDLE) &SwSmiRecord->Link, &SwSmiRecord->Context, &SwSmiCommBuffer, &SwSmiCommBufferSize); + } + LinkInDb = GetNextNode (&mSwSmiCallbackDataBase, &SwSmiRecord->Link); + } + } + + return EFI_SUCCESS; +} + +/** + Init required protocol for Pch Sw Dispatch protocol. +**/ +VOID +PchSwDispatchInit ( + VOID + ) +{ + EFI_STATUS Status; + EFI_HANDLE DispatchHandle; + DATABASE_RECORD Record; + + // + // Locate PI SMM CPU protocol + // + Status = gSmst->SmmLocateProtocol (&gEfiSmmCpuProtocolGuid, NULL, (VOID **)&mSmmCpuProtocol); + ASSERT_EFI_ERROR (Status); + + // + // Initialize SW SMI Callback DataBase + // + InitializeListHead (&mSwSmiCallbackDataBase); + + // + // Insert SwSmi handler to PchSmmCore database + // There will always be one SwType record in PchSmmCore database + // + ZeroMem (&Record, sizeof (DATABASE_RECORD)); + Record.Signature = DATABASE_RECORD_SIGNATURE; + Record.Callback = PchSwSmiDispatcher; + Record.ProtocolType = SwType; + + CopyMem (&Record.SrcDesc, &mSwSourceDesc, sizeof (PCH_SMM_SOURCE_DESC)); + + DispatchHandle = NULL; + Status = SmmCoreInsertRecord ( + &Record, + &DispatchHandle + ); + ASSERT_EFI_ERROR (Status); +} diff --git a/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmSx.c b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmSx.c new file mode 100644 index 0000000000..798fb33347 --- /dev/null +++ b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmSx.c @@ -0,0 +1,117 @@ +/** @file + File to contain all the hardware specific stuff for the Smm Sx dispatch protocol. + + Copyright (c) 2022, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ +#include "PchSmmHelpers.h" +#include +#include "PchSmiHelper.h" + +extern BOOLEAN mS3SusStart; +#define PROGRESS_CODE_S3_SUSPEND_END PcdGet32 (PcdProgressCodeS3SuspendEnd) + +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSxSourceDesc = { + PCH_SMM_NO_FLAGS, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_EN} + }, + S_ACPI_IO_SMI_EN, + N_ACPI_IO_SMI_EN_ON_SLP_EN + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_ON_SLP_EN + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_ON_SLP_EN + } +}; + +/** + Get the Sleep type + + @param[in] Record No use + @param[out] Context The context that includes SLP_TYP bits to be filled + +**/ +VOID +EFIAPI +SxGetContext ( + IN DATABASE_RECORD *Record, + OUT PCH_SMM_CONTEXT *Context + ) +{ + UINT32 Pm1Cnt; + + Pm1Cnt = IoRead32 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_PM1_CNT)); + + /// + /// By design, the context phase will always be ENTRY + /// + Context->Sx.Phase = SxEntry; + + /// + /// Map the PM1_CNT register's SLP_TYP bits to the context type + /// + switch (Pm1Cnt & B_ACPI_IO_PM1_CNT_SLP_TYP) { + case V_ACPI_IO_PM1_CNT_S0: + Context->Sx.Type = SxS0; + break; + + case V_ACPI_IO_PM1_CNT_S1: + Context->Sx.Type = SxS1; + break; + + case V_ACPI_IO_PM1_CNT_S3: + Context->Sx.Type = SxS3; + break; + + case V_ACPI_IO_PM1_CNT_S4: + Context->Sx.Type = SxS4; + break; + + case V_ACPI_IO_PM1_CNT_S5: + Context->Sx.Type = SxS5; + break; + + default: + ASSERT (FALSE); + break; + } +} + +/** + Check whether sleep type of two contexts match + + @param[in] Context1 Context 1 that includes sleep type 1 + @param[in] Context2 Context 2 that includes sleep type 2 + + @retval FALSE Sleep types match + @retval TRUE Sleep types don't match +**/ +BOOLEAN +EFIAPI +SxCmpContext ( + IN PCH_SMM_CONTEXT *Context1, + IN PCH_SMM_CONTEXT *Context2 + ) +{ + return (BOOLEAN) (Context1->Sx.Type == Context2->Sx.Type); +} diff --git a/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmUsb.c b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmUsb.c new file mode 100644 index 0000000000..6b23956c4a --- /dev/null +++ b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmUsb.c @@ -0,0 +1,244 @@ +/** @file + File to contain all the hardware specific stuff for the Smm USB dispatch protocol. + + Copyright (c) 2022, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ +#include "PchSmmHelpers.h" +#include +#include + +typedef enum { + PchUsbControllerLpc0 = 0, + PchUsbControllerXhci, + PchUsbControllerTypeMax +} PCH_USB_CONTROLLER_TYPE; + +typedef struct { + UINT8 Function; + UINT8 Device; + PCH_USB_CONTROLLER_TYPE UsbConType; +} PCH_USB_CONTROLLER; + +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mUsb1Legacy = { + PCH_SMM_NO_FLAGS, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_EN} + }, + S_ACPI_IO_SMI_EN, + N_ACPI_IO_SMI_EN_LEGACY_USB + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_LEGACY_USB + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_LEGACY_USB + } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mUsb3Legacy = { + PCH_SMM_NO_FLAGS, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_EN} + }, + S_ACPI_IO_SMI_EN, + N_ACPI_IO_SMI_EN_LEGACY_USB3 + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_LEGACY_USB3 + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_LEGACY_USB3 + } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED PCH_USB_CONTROLLER mUsbControllersMap[] = { + { + 0xFF, // to be updated in PchSmmUsbUpdateDescriptors + 0xFF, // to be updated in PchSmmUsbUpdateDescriptors + PchUsbControllerLpc0 + }, + { + 0xFF, // to be updated in PchSmmUsbUpdateDescriptors + 0xFF, // to be updated in PchSmmUsbUpdateDescriptors + PchUsbControllerXhci + } +}; + +/** + Performs update of SmmUsb descriptors with values that have to be evaluated during runtime. +**/ +VOID +PchSmmUsbUpdateDescriptors ( + VOID + ) +{ + // + // mUsbControllersMap + // + mUsbControllersMap[0].Function = LpcFuncNumber (); + mUsbControllersMap[0].Device = LpcDevNumber (); + mUsbControllersMap[1].Function = PchXhciFuncNumber (); + mUsbControllersMap[1].Device = PchXhciDevNumber (); +} + +/** + Find the handle that best matches the input Device Path and return the USB controller type + + @param[in] DevicePath Pointer to the device Path table + @param[out] Controller Returned with the USB controller type of the input device path + + @retval EFI_SUCCESS Find the handle that best matches the input Device Path + @exception EFI_UNSUPPORTED Invalid device Path table or can't find any match USB device path + PCH_USB_CONTROLLER_TYPE The USB controller type of the input + device path +**/ +EFI_STATUS +DevicePathToSupportedController ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT PCH_USB_CONTROLLER_TYPE *Controller + ) +{ + EFI_STATUS Status; + EFI_HANDLE DeviceHandle; + ACPI_HID_DEVICE_PATH *AcpiNode; + PCI_DEVICE_PATH *PciNode; + EFI_DEVICE_PATH_PROTOCOL *RemaingDevicePath; + UINT8 UsbIndex; + /// + /// Find the handle that best matches the Device Path. If it is only a + /// partial match the remaining part of the device path is returned in + /// RemainingDevicePath. + /// + RemaingDevicePath = DevicePath; + Status = gBS->LocateDevicePath ( + &gEfiPciRootBridgeIoProtocolGuid, + &DevicePath, + &DeviceHandle + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + DevicePath = RemaingDevicePath; + + /// + /// Get first node: Acpi Node + /// + AcpiNode = (ACPI_HID_DEVICE_PATH *) RemaingDevicePath; + + if (AcpiNode->Header.Type != ACPI_DEVICE_PATH || + AcpiNode->Header.SubType != ACPI_DP || + DevicePathNodeLength (&AcpiNode->Header) != sizeof (ACPI_HID_DEVICE_PATH) || + AcpiNode->HID != EISA_PNP_ID (0x0A03) || + AcpiNode->UID != 0 + ) { + return EFI_UNSUPPORTED; + } else { + /// + /// Get the next node: Pci Node + /// + RemaingDevicePath = NextDevicePathNode (RemaingDevicePath); + PciNode = (PCI_DEVICE_PATH *) RemaingDevicePath; + if (PciNode->Header.Type != HARDWARE_DEVICE_PATH || + PciNode->Header.SubType != HW_PCI_DP || + DevicePathNodeLength (&PciNode->Header) != sizeof (PCI_DEVICE_PATH) + ) { + return EFI_UNSUPPORTED; + } + + for (UsbIndex = 0; UsbIndex < sizeof (mUsbControllersMap) / sizeof (PCH_USB_CONTROLLER); UsbIndex++) { + if ((PciNode->Device == mUsbControllersMap[UsbIndex].Device) && + (PciNode->Function == mUsbControllersMap[UsbIndex].Function)) { + *Controller = mUsbControllersMap[UsbIndex].UsbConType; + return EFI_SUCCESS; + } + } + + return EFI_UNSUPPORTED; + } +} + +/** + Maps a USB context to a source description. + + @param[in] Context The context we need to map. Type must be USB. + @param[in] SrcDesc The source description that corresponds to the given context. + +**/ +VOID +MapUsbToSrcDesc ( + IN PCH_SMM_CONTEXT *Context, + OUT PCH_SMM_SOURCE_DESC *SrcDesc + ) +{ + PCH_USB_CONTROLLER_TYPE Controller; + EFI_STATUS Status; + + Status = DevicePathToSupportedController (Context->Usb.Device, &Controller); + /// + /// Either the device path passed in by the child is incorrect or + /// the ones stored here internally are incorrect. + /// + ASSERT_EFI_ERROR (Status); + + switch (Context->Usb.Type) { + case UsbLegacy: + switch (Controller) { + case PchUsbControllerLpc0: + CopyMem ((VOID *) SrcDesc, (VOID *) (&mUsb1Legacy), sizeof (PCH_SMM_SOURCE_DESC)); + break; + + case PchUsbControllerXhci: + CopyMem ((VOID *) SrcDesc, (VOID *) (&mUsb3Legacy), sizeof (PCH_SMM_SOURCE_DESC)); + break; + + default: + ASSERT (FALSE); + break; + } + break; + + case UsbWake: + ASSERT (FALSE); + break; + + default: + ASSERT (FALSE); + break; + } +} diff --git a/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchxSmmHelpers.c b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchxSmmHelpers.c new file mode 100644 index 0000000000..c0ce5785fd --- /dev/null +++ b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchxSmmHelpers.c @@ -0,0 +1,682 @@ +/** @file + This driver is responsible for the registration of child drivers + and the abstraction of the PCH SMI sources. + + Copyright (c) 2022, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ +#include "PchSmmHelpers.h" +#include +#include +#include + +extern UINT32 mTco1StsClear; +// +// Help handle porting bit shifts to IA-64. +// +#define BIT_ZERO 0x00000001 + +/** + Publish SMI Dispatch protocols. + + +**/ +VOID +PchSmmPublishDispatchProtocols ( + VOID + ) +{ + EFI_STATUS Status = EFI_SUCCESS; + UINTN Index; + // + // Install protocol interfaces. + // + for (Index = 0; Index < PCH_SMM_PROTOCOL_TYPE_MAX; Index++) { + Status = gSmst->SmmInstallProtocolInterface ( + &mPrivateData.InstallMultProtHandle, + mPrivateData.Protocols[Index].Guid, + EFI_NATIVE_INTERFACE, + &mPrivateData.Protocols[Index].Protocols.Generic + ); + } + ASSERT_EFI_ERROR (Status); +} + +/** + Initialize bits that aren't necessarily related to an SMI source. + + + @retval EFI_SUCCESS SMI source initialization completed. + @retval Asserts Global Smi Bit is not enabled successfully. +**/ +EFI_STATUS +PchSmmInitHardware ( + VOID + ) +{ + EFI_STATUS Status; + + // + // Clear all SMIs + // + PchSmmClearSmi (); + + Status = PchSmmEnableGlobalSmiBit (); + ASSERT_EFI_ERROR (Status); + + // + // Be *really* sure to clear all SMIs + // + PchSmmClearSmi (); + + return EFI_SUCCESS; +} + +/** + Enables the PCH to generate SMIs. Note that no SMIs will be generated + if no SMI sources are enabled. Conversely, no enabled SMI source will + generate SMIs if SMIs are not globally enabled. This is the main + switchbox for SMI generation. + + + @retval EFI_SUCCESS Enable Global Smi Bit completed +**/ +EFI_STATUS +PchSmmEnableGlobalSmiBit ( + VOID + ) +{ + UINT32 SmiEn; + + SmiEn = IoRead32 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_SMI_EN)); + + // + // Set the "global smi enable" bit + // + SmiEn |= B_ACPI_IO_SMI_EN_GBL_SMI; + + IoWrite32 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_SMI_EN), SmiEn); + + return EFI_SUCCESS; +} + +/** + Clears the SMI after all SMI source have been processed. + Note that this function will not work correctly (as it is + written) unless all SMI sources have been processed. + A revision of this function could manually clear all SMI + status bits to guarantee success. + + + @retval EFI_SUCCESS Clears the SMIs completed + @retval Asserts EOS was not set to a 1 +**/ +EFI_STATUS +PchSmmClearSmi ( + VOID + ) +{ + return EFI_SUCCESS; +} + +/** + Set the SMI EOS bit after all SMI source have been processed. + + + @retval FALSE EOS was not set to a 1; this is an error + @retval TRUE EOS was correctly set to a 1 +**/ +BOOLEAN +PchSmmSetAndCheckEos ( + VOID + ) +{ + UINT32 SmiEn; + + SmiEn = IoRead32 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_SMI_EN)); + + // + // Reset the PCH to generate subsequent SMIs + // + SmiEn |= B_ACPI_IO_SMI_EN_EOS; + + IoWrite32 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_SMI_EN), SmiEn); + + // + // Double check that the assert worked + // + SmiEn = IoRead32 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_SMI_EN)); + + // + // Return TRUE if EOS is set correctly + // + if ((SmiEn & B_ACPI_IO_SMI_EN_EOS) == 0) { + // + // EOS was not set to a 1; this is an error + // + return FALSE; + } else { + // + // EOS was correctly set to a 1 + // + return TRUE; + } +} + +/** + Determine whether an ACPI OS is present (via the SCI_EN bit) + + + @retval TRUE ACPI OS is present + @retval FALSE ACPI OS is not present +**/ +BOOLEAN +PchSmmGetSciEn ( + VOID + ) +{ + BOOLEAN SciEn; + UINT32 Pm1Cnt; + + // + // Determine whether an ACPI OS is present (via the SCI_EN bit) + // + Pm1Cnt = IoRead32 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_PM1_CNT)); + SciEn = (BOOLEAN) ((Pm1Cnt & B_ACPI_IO_PM1_CNT_SCI_EN) == B_ACPI_IO_PM1_CNT_SCI_EN); + + return SciEn; +} + +/** + Read a specifying bit with the register + These may or may not need to change w/ the PCH version; they're highly IA-32 dependent, though. + + @param[in] BitDesc The struct that includes register address, size in byte and bit number + + @retval TRUE The bit is enabled + @retval FALSE The bit is disabled +**/ +BOOLEAN +ReadBitDesc ( + CONST PCH_SMM_BIT_DESC *BitDesc + ) +{ + EFI_STATUS Status; + UINT64 Register; + UINT32 PciBus; + UINT32 PciDev; + UINT32 PciFun; + UINT32 PciReg; + UINTN RegSize; + BOOLEAN BitWasOne; + UINTN ShiftCount; + UINTN RegisterOffset; + UINT32 BaseAddr; + UINT64 PciBaseAddress; + + ASSERT (BitDesc != NULL); + if (BitDesc == NULL) { + return FALSE; + } + ASSERT (!IS_BIT_DESC_NULL (*BitDesc)); + + RegSize = 0; + Register = 0; + ShiftCount = 0; + BitWasOne = FALSE; + + switch (BitDesc->Reg.Type) { + + case ACPI_ADDR_TYPE: + case TCO_ADDR_TYPE: + if (BitDesc->Reg.Type == ACPI_ADDR_TYPE) { + RegisterOffset = BitDesc->Reg.Data.acpi; + BaseAddr = mAcpiBaseAddr; + } else { + RegisterOffset = BitDesc->Reg.Data.tco; + BaseAddr = mTcoBaseAddr; + } + switch (BitDesc->SizeInBytes) { + + case 0: + // + // Chances are that this field didn't get initialized. + // Check your assignments to bit descriptions. + // + ASSERT (FALSE); + break; + + case 1: + RegSize = SMM_IO_UINT8; + break; + + case 2: + RegSize = SMM_IO_UINT16; + break; + + case 4: + RegSize = SMM_IO_UINT32; + break; + + case 8: + RegSize = SMM_IO_UINT64; + break; + + default: + // + // Unsupported or invalid register size + // + ASSERT (FALSE); + break; + } + // + // Double check that we correctly read in the acpi base address + // + ASSERT ((BaseAddr != 0x0) && ((BaseAddr & 0x1) != 0x1)); + + ShiftCount = BitDesc->Bit; + // + // As current CPU Smm Io can only support at most + // 32-bit read/write,if Operation is 64 bit, + // we do a 32 bit operation according to BitDesc->Bit + // + if (RegSize == SMM_IO_UINT64) { + RegSize = SMM_IO_UINT32; + // + // If the operation is for high 32 bits + // + if (BitDesc->Bit >= 32) { + RegisterOffset += 4; + ShiftCount -= 32; + } + } + + Status = gSmst->SmmIo.Io.Read ( + &gSmst->SmmIo, + RegSize, + BaseAddr + RegisterOffset, + 1, + &Register + ); + ASSERT_EFI_ERROR (Status); + + if ((Register & (LShiftU64 (BIT_ZERO, ShiftCount))) != 0) { + BitWasOne = TRUE; + } else { + BitWasOne = FALSE; + } + break; + + case GPIO_ADDR_TYPE: + case MEMORY_MAPPED_IO_ADDRESS_TYPE: + // + // Read the register, and it with the bit to read + // + switch (BitDesc->SizeInBytes) { + case 1: + Register = (UINT64) MmioRead8 ((UINTN) BitDesc->Reg.Data.Mmio); + break; + + case 2: + Register = (UINT64) MmioRead16 ((UINTN) BitDesc->Reg.Data.Mmio); + break; + + case 4: + Register = (UINT64) MmioRead32 ((UINTN) BitDesc->Reg.Data.Mmio); + break; + + case 8: + Register = (UINT64) MmioRead32 ((UINTN) BitDesc->Reg.Data.Mmio); + *((UINT32 *) (&Register) + 1) = MmioRead32 ((UINTN) BitDesc->Reg.Data.Mmio + 4); + break; + + default: + // + // Unsupported or invalid register size + // + ASSERT (FALSE); + break; + } + + Register = Register & (LShiftU64 (BIT0, BitDesc->Bit)); + if (Register) { + BitWasOne = TRUE; + } else { + BitWasOne = FALSE; + } + break; + + case PCIE_ADDR_TYPE: + PciBus = BitDesc->Reg.Data.pcie.Fields.Bus; + PciDev = BitDesc->Reg.Data.pcie.Fields.Dev; + PciFun = BitDesc->Reg.Data.pcie.Fields.Fnc; + PciReg = BitDesc->Reg.Data.pcie.Fields.Reg; + PciBaseAddress = PCI_SEGMENT_LIB_ADDRESS (DEFAULT_PCI_SEGMENT_NUMBER_PCH, PciBus, PciDev, PciFun, 0); + switch (BitDesc->SizeInBytes) { + + case 0: + // + // Chances are that this field didn't get initialized. + // Check your assignments to bit descriptions. + // + ASSERT (FALSE); + break; + + case 1: + Register = (UINT64) PciSegmentRead8 (PciBaseAddress + PciReg); + break; + + case 2: + Register = (UINT64) PciSegmentRead16 (PciBaseAddress + PciReg); + break; + + case 4: + Register = (UINT64) PciSegmentRead32 (PciBaseAddress + PciReg); + break; + + default: + // + // Unsupported or invalid register size + // + ASSERT (FALSE); + break; + } + + if ((Register & (LShiftU64 (BIT_ZERO, BitDesc->Bit))) != 0) { + BitWasOne = TRUE; + } else { + BitWasOne = FALSE; + } + break; + + case PCR_ADDR_TYPE: + // + // Read the register, and it with the bit to read + // + switch (BitDesc->SizeInBytes) { + case 1: + Register = PchPcrRead8 (BitDesc->Reg.Data.Pcr.Fields.Pid, BitDesc->Reg.Data.Pcr.Fields.Offset); + break; + + case 2: + Register = PchPcrRead16 (BitDesc->Reg.Data.Pcr.Fields.Pid, BitDesc->Reg.Data.Pcr.Fields.Offset); + break; + + case 4: + Register = PchPcrRead32 (BitDesc->Reg.Data.Pcr.Fields.Pid, BitDesc->Reg.Data.Pcr.Fields.Offset); + break; + + default: + // + // Unsupported or invalid register size + // + ASSERT (FALSE); + break; + } + + Register = Register & (LShiftU64 (BIT0, BitDesc->Bit)); + if (Register) { + BitWasOne = TRUE; + } else { + BitWasOne = FALSE; + } + break; + + default: + // + // This address type is not yet implemented + // + ASSERT (FALSE); + break; + } + + return BitWasOne; +} + +/** + Write a specifying bit with the register + + @param[in] BitDesc The struct that includes register address, size in byte and bit number + @param[in] ValueToWrite The value to be wrote + @param[in] WriteClear If the rest bits of the register is write clear + +**/ +VOID +WriteBitDesc ( + CONST PCH_SMM_BIT_DESC *BitDesc, + CONST BOOLEAN ValueToWrite, + CONST BOOLEAN WriteClear + ) +{ + EFI_STATUS Status; + UINT64 Register; + UINT64 AndVal; + UINT64 OrVal; + UINT32 RegSize; + UINT32 PciBus; + UINT32 PciDev; + UINT32 PciFun; + UINT32 PciReg; + UINTN RegisterOffset; + UINT32 BaseAddr; + UINT64 PciBaseAddress; + + ASSERT (BitDesc != NULL); + if (BitDesc == NULL) { + return; + } + ASSERT (!IS_BIT_DESC_NULL (*BitDesc)); + + RegSize = 0; + Register = 0; + + if (WriteClear) { + AndVal = LShiftU64 (BIT_ZERO, BitDesc->Bit); + } else { + AndVal = ~(LShiftU64 (BIT_ZERO, BitDesc->Bit)); + } + + OrVal = (LShiftU64 ((UINT32) ValueToWrite, BitDesc->Bit)); + + switch (BitDesc->Reg.Type) { + + case ACPI_ADDR_TYPE: + case TCO_ADDR_TYPE: + if (BitDesc->Reg.Type == ACPI_ADDR_TYPE) { + RegisterOffset = BitDesc->Reg.Data.acpi; + BaseAddr = mAcpiBaseAddr; + } else { + RegisterOffset = BitDesc->Reg.Data.tco; + BaseAddr = mTcoBaseAddr; + } + + switch (BitDesc->SizeInBytes) { + + case 0: + // + // Chances are that this field didn't get initialized. + // Check your assignments to bit descriptions. + // + ASSERT (FALSE); + break; + + case 1: + RegSize = SMM_IO_UINT8; + break; + + case 2: + RegSize = SMM_IO_UINT16; + break; + + case 4: + RegSize = SMM_IO_UINT32; + break; + + case 8: + RegSize = SMM_IO_UINT64; + break; + + default: + // + // Unsupported or invalid register size + // + ASSERT (FALSE); + break; + } + // + // Double check that we correctly read in the acpi base address + // + ASSERT ((BaseAddr != 0x0) && ((BaseAddr & 0x1) != 0x1)); + + // + // As current CPU Smm Io can only support at most + // 32-bit read/write,if Operation is 64 bit, + // we do a 32 bit operation according to BitDesc->Bit + // + if (RegSize == SMM_IO_UINT64) { + RegSize = SMM_IO_UINT32; + // + // If the operation is for high 32 bits + // + if (BitDesc->Bit >= 32) { + RegisterOffset += 4; + + if (WriteClear) { + AndVal = LShiftU64 (BIT_ZERO, BitDesc->Bit - 32); + } else { + AndVal = ~(LShiftU64 (BIT_ZERO, BitDesc->Bit - 32)); + } + + OrVal = LShiftU64 ((UINT32) ValueToWrite, BitDesc->Bit - 32); + } + } + + Status = gSmst->SmmIo.Io.Read ( + &gSmst->SmmIo, + RegSize, + BaseAddr + RegisterOffset, + 1, + &Register + ); + ASSERT_EFI_ERROR (Status); + + Register &= AndVal; + Register |= OrVal; + + Status = gSmst->SmmIo.Io.Write ( + &gSmst->SmmIo, + RegSize, + BaseAddr + RegisterOffset, + 1, + &Register + ); + ASSERT_EFI_ERROR (Status); + break; + + case GPIO_ADDR_TYPE: + case MEMORY_MAPPED_IO_ADDRESS_TYPE: + // + // Read the register, or it with the bit to set, then write it back. + // + switch (BitDesc->SizeInBytes) { + case 1: + MmioAndThenOr8 ((UINTN) BitDesc->Reg.Data.Mmio, (UINT8) AndVal, (UINT8) OrVal); + break; + + case 2: + MmioAndThenOr16 ((UINTN) BitDesc->Reg.Data.Mmio, (UINT16) AndVal, (UINT16) OrVal); + break; + + case 4: + MmioAndThenOr32 ((UINTN) BitDesc->Reg.Data.Mmio, (UINT32) AndVal, (UINT32) OrVal); + break; + + case 8: + Register = (UINT64) MmioRead32 ((UINTN) BitDesc->Reg.Data.Mmio); + *((UINT32 *) (&Register) + 1) = MmioRead32 ((UINTN) BitDesc->Reg.Data.Mmio + 4); + Register &= AndVal; + Register |= OrVal; + MmioWrite32 ((UINTN) BitDesc->Reg.Data.Mmio, (UINT32) Register); + MmioWrite32 ((UINTN) BitDesc->Reg.Data.Mmio + 4, *((UINT32 *) (&Register) + 1)); + break; + + default: + // + // Unsupported or invalid register size + // + ASSERT (FALSE); + break; + } + break; + + case PCIE_ADDR_TYPE: + PciBus = BitDesc->Reg.Data.pcie.Fields.Bus; + PciDev = BitDesc->Reg.Data.pcie.Fields.Dev; + PciFun = BitDesc->Reg.Data.pcie.Fields.Fnc; + PciReg = BitDesc->Reg.Data.pcie.Fields.Reg; + PciBaseAddress = PCI_SEGMENT_LIB_ADDRESS (DEFAULT_PCI_SEGMENT_NUMBER_PCH, PciBus, PciDev, PciFun, 0); + switch (BitDesc->SizeInBytes) { + + case 0: + // + // Chances are that this field didn't get initialized -- check your assignments + // to bit descriptions. + // + ASSERT (FALSE); + break; + + case 1: + PciSegmentAndThenOr8 (PciBaseAddress + PciReg, (UINT8) AndVal, (UINT8) OrVal); + break; + + case 2: + PciSegmentAndThenOr16 (PciBaseAddress + PciReg, (UINT16) AndVal, (UINT16) OrVal); + break; + + case 4: + PciSegmentAndThenOr32 (PciBaseAddress + PciReg, (UINT32) AndVal, (UINT32) OrVal); + break; + + default: + // + // Unsupported or invalid register size + // + ASSERT (FALSE); + break; + } + break; + + case PCR_ADDR_TYPE: + // + // Read the register, or it with the bit to set, then write it back. + // + switch (BitDesc->SizeInBytes) { + case 1: + PchPcrAndThenOr8 ((PCH_SBI_PID) BitDesc->Reg.Data.Pcr.Fields.Pid, (UINT16) BitDesc->Reg.Data.Pcr.Fields.Offset, (UINT8) AndVal, (UINT8) OrVal); + break; + + case 2: + PchPcrAndThenOr16 ((PCH_SBI_PID) BitDesc->Reg.Data.Pcr.Fields.Pid, (UINT16) BitDesc->Reg.Data.Pcr.Fields.Offset, (UINT16) AndVal, (UINT16) OrVal); + break; + + case 4: + PchPcrAndThenOr32 ((PCH_SBI_PID) BitDesc->Reg.Data.Pcr.Fields.Pid, (UINT16) BitDesc->Reg.Data.Pcr.Fields.Offset, (UINT32) AndVal, (UINT32) OrVal); + break; + + default: + // + // Unsupported or invalid register size + // + ASSERT (FALSE); + break; + } + break; + + default: + // + // This address type is not yet implemented + // + ASSERT (FALSE); + break; + } +} diff --git a/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchxSmmHelpers.h b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchxSmmHelpers.h new file mode 100644 index 0000000000..998b38e159 --- /dev/null +++ b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchxSmmHelpers.h @@ -0,0 +1,107 @@ +/** @file + This driver is responsible for the registration of child drivers + and the abstraction of the PCH SMI sources. + + Copyright (c) 2022, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ +#ifndef _PCHX_SMM_HELPERS_H_ +#define _PCHX_SMM_HELPERS_H_ + +#include "PchSmm.h" + +/** + Initialize bits that aren't necessarily related to an SMI source. + + + @retval EFI_SUCCESS SMI source initialization completed. + @retval Asserts Global Smi Bit is not enabled successfully. +**/ +EFI_STATUS +PchSmmInitHardware ( + VOID + ); + +/** + Enables the PCH to generate SMIs. Note that no SMIs will be generated + if no SMI sources are enabled. Conversely, no enabled SMI source will + generate SMIs if SMIs are not globally enabled. This is the main + switchbox for SMI generation. + + + @retval EFI_SUCCESS Enable Global Smi Bit completed +**/ +EFI_STATUS +PchSmmEnableGlobalSmiBit ( + VOID + ); + +/** + Clears the SMI after all SMI source have been processed. + Note that this function will not work correctly (as it is + written) unless all SMI sources have been processed. + A revision of this function could manually clear all SMI + status bits to guarantee success. + + + @retval EFI_SUCCESS Clears the SMIs completed + @retval Asserts EOS was not set to a 1 +**/ +EFI_STATUS +PchSmmClearSmi ( + VOID + ); + +/** + Set the SMI EOS bit after all SMI source have been processed. + + + @retval FALSE EOS was not set to a 1; this is an error + @retval TRUE EOS was correctly set to a 1 +**/ +BOOLEAN +PchSmmSetAndCheckEos ( + VOID + ); + +/** + Determine whether an ACPI OS is present (via the SCI_EN bit) + + + @retval TRUE ACPI OS is present + @retval FALSE ACPI OS is not present +**/ +BOOLEAN +PchSmmGetSciEn ( + VOID + ); + +/** + Read a specifying bit with the register + + @param[in] BitDesc The struct that includes register address, size in byte and bit number + + @retval TRUE The bit is enabled + @retval FALSE The bit is disabled +**/ +BOOLEAN +ReadBitDesc ( + CONST PCH_SMM_BIT_DESC *BitDesc + ); + +/** + Write a specifying bit with the register + + @param[in] BitDesc The struct that includes register address, size in byte and bit number + @param[in] ValueToWrite The value to be wrote + @param[in] WriteClear If the rest bits of the register is write clear + +**/ +VOID +WriteBitDesc ( + CONST PCH_SMM_BIT_DESC *BitDesc, + CONST BOOLEAN ValueToWrite, + CONST BOOLEAN WriteClear + ); + +#endif diff --git a/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PcieSmmClient.c b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PcieSmmClient.c new file mode 100644 index 0000000000..6d210f671b --- /dev/null +++ b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PcieSmmClient.c @@ -0,0 +1,38 @@ +/** @file + This function handle the register/unregister of PCH PCIe specific SMI events. + + Copyright (c) 2022, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ +#include "PchSmmHelpers.h" +#include +#include +#include +#include +#include +#include +#include + +extern UINT32 mNumOfRootPorts; + +/** + Get CPU or PCH Pcie Root Port Device and Function Number by Root Port physical Number + + @param[in] RpNumber Root port physical number. (0-based) + @param[out] RpDev Return corresponding root port device number. + @param[out] RpFun Return corresponding root port function number. +**/ +VOID +GetPcieRpDevFun ( + IN UINTN RpIndex, + OUT UINTN *RpDev, + OUT UINTN *RpFun + ) +{ + if (RpIndex >= CpuRpIndex0 && RpIndex <= CpuRpIndex3) { + GetCpuPcieRpDevFun ((RpIndex - CpuRpIndex0), RpDev, RpFun); + } else { + *RpDev = PchPcieRpDevNumber (RpIndex); + *RpFun = PchPcieRpFuncNumber (RpIndex); + } +} diff --git a/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/SmmGlobalsPch.c b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/SmmGlobalsPch.c new file mode 100644 index 0000000000..f66079884f --- /dev/null +++ b/Silicon/Intel/AlderlakeSiliconPkg/Pch/PchSmiDispatcher/Smm/SmmGlobalsPch.c @@ -0,0 +1,20 @@ +/** @file + This driver is responsible for the registration of child drivers + and the abstraction of the PCH SMI sources. + + Copyright (c) 2022, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ +#include + +GLOBAL_REMOVE_IF_UNREFERENCED UINT32 mTco1StsClear = + ( + B_TCO_IO_TCO1_STS_DMISERR | + B_TCO_IO_TCO1_STS_DMISMI | + B_TCO_IO_TCO1_STS_DMISCI | + B_TCO_IO_TCO1_STS_BIOSWR | + B_TCO_IO_TCO1_STS_NEWCENTURY | + B_TCO_IO_TCO1_STS_TIMEOUT | + B_TCO_IO_TCO1_STS_TCO_INT | + B_TCO_IO_TCO1_STS_SW_TCO_SMI + ); diff --git a/Silicon/Intel/AlderlakeSiliconPkg/Pch/SmmControl/RuntimeDxe/SmmControl.inf b/Silicon/Intel/AlderlakeSiliconPkg/Pch/SmmControl/RuntimeDxe/SmmControl.inf new file mode 100644 index 0000000000..d73c12f964 --- /dev/null +++ b/Silicon/Intel/AlderlakeSiliconPkg/Pch/SmmControl/RuntimeDxe/SmmControl.inf @@ -0,0 +1,53 @@ +## @file +# Component description file for SmmControl module +# +# Copyright (c) 2022, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +## + + +[Defines] +INF_VERSION = 0x00010017 +BASE_NAME = SmmControl +FILE_GUID = A0BAD9F7-AB78-491b-B583-C52B7F84B9E0 +VERSION_STRING = 1.0 +MODULE_TYPE = DXE_RUNTIME_DRIVER +ENTRY_POINT = SmmControlDriverEntryInit +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + + + +[LibraryClasses] +IoLib +UefiDriverEntryPoint +DebugLib +UefiBootServicesTableLib +UefiRuntimeServicesTableLib +PmcLib +GpioLib + + +[Packages] +MdePkg/MdePkg.dec +AlderlakeSiliconPkg/SiPkg.dec + + +[Sources] +SmmControlDriver.h +SmmControlDriver.c + + +[Protocols] +gEfiSmmControl2ProtocolGuid ## PRODUCES + + +[Guids] +gEfiEventVirtualAddressChangeGuid + + +[Depex] +TRUE diff --git a/Silicon/Intel/AlderlakeSiliconPkg/Pch/SmmControl/RuntimeDxe/SmmControlDriver.c b/Silicon/Intel/AlderlakeSiliconPkg/Pch/SmmControl/RuntimeDxe/SmmControlDriver.c new file mode 100644 index 0000000000..8864d18787 --- /dev/null +++ b/Silicon/Intel/AlderlakeSiliconPkg/Pch/SmmControl/RuntimeDxe/SmmControlDriver.c @@ -0,0 +1,338 @@ +/** @file + This is the driver that publishes the SMM Control Protocol. + + Copyright (c) 2022, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "SmmControlDriver.h" + +STATIC SMM_CONTROL_PRIVATE_DATA mSmmControl; +GLOBAL_REMOVE_IF_UNREFERENCED UINT16 mABase; + +/** + Fixup internal data pointers so that the services can be called in virtual mode. + + @param[in] Event The event registered. + @param[in] Context Event context. + +**/ +VOID +EFIAPI +SmmControlVirtualAddressChangeEvent ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + gRT->ConvertPointer (0, (VOID *) &(mSmmControl.SmmControl.Trigger)); + gRT->ConvertPointer (0, (VOID *) &(mSmmControl.SmmControl.Clear)); +} + +/** + SmmControl DXE RUNTIME Module Entry Point\n + - Introduction\n + The SmmControl module is a DXE RUNTIME driver that provides a standard way + for other drivers to trigger software SMIs. + + - @pre + - PCH Power Management I/O space base address has already been programmed. + If SmmControl Runtime DXE driver is run before Status Code Runtime Protocol + is installed and there is the need to use Status code in the driver, it will + be necessary to add EFI_STATUS_CODE_RUNTIME_PROTOCOL_GUID to the dependency file. + - EFI_SMM_BASE2_PROTOCOL + - Documented in the System Management Mode Core Interface Specification. + + - @result + The SmmControl driver produces the EFI_SMM_CONTROL_PROTOCOL documented in + System Management Mode Core Interface Specification. + + @param[in] ImageHandle Handle for the image of this driver + @param[in] SystemTable Pointer to the EFI System Table + + @retval EFI_STATUS Results of the installation of the SMM Control Protocol +**/ +EFI_STATUS +EFIAPI +SmmControlDriverEntryInit ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_EVENT Event; + + DEBUG ((DEBUG_INFO, "SmmControlDriverEntryInit() Start\n")); + + // + // Get the Power Management I/O space base address. We assume that + // this base address has already been programmed if this driver is + // being run. + // + mABase = PmcGetAcpiBase (); + + Status = EFI_SUCCESS; + if (mABase != 0) { + // + // Install the instance of the protocol + // + mSmmControl.Signature = SMM_CONTROL_PRIVATE_DATA_SIGNATURE; + mSmmControl.Handle = ImageHandle; + + mSmmControl.SmmControl.Trigger = Activate; + mSmmControl.SmmControl.Clear = Deactivate; + mSmmControl.SmmControl.MinimumTriggerPeriod = 0; + + // + // Install our protocol interfaces on the device's handle + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &mSmmControl.Handle, + &gEfiSmmControl2ProtocolGuid, + &mSmmControl.SmmControl, + NULL + ); + } else { + Status = EFI_DEVICE_ERROR; + return Status; + } + + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + SmmControlVirtualAddressChangeEvent, + NULL, + &gEfiEventVirtualAddressChangeGuid, + &Event + ); + + DEBUG ((DEBUG_INFO, "SmmControlDriverEntryInit() End\n")); + + return Status; +} + +/** + Trigger the software SMI + + @param[in] Command The value to be set on the software SMI command port + @param[in] Data The value to be set on the software SMI data port + + @retval EFI_SUCCESS Function completes successfully +**/ +EFI_STATUS +EFIAPI +SmmTrigger ( + IN UINT8 Command, + IN UINT8 Data + ) +{ + UINT32 OutputData; + UINT32 OutputPort; + + // + // Enable the APMC SMI + // + OutputPort = mABase + R_ACPI_IO_SMI_EN; + OutputData = IoRead32 ((UINTN) OutputPort); + OutputData |= (B_ACPI_IO_SMI_EN_APMC | B_ACPI_IO_SMI_EN_GBL_SMI); + DEBUG ( + (DEBUG_VERBOSE, + "The SMI Control Port at address %x will be written to %x.\n", + OutputPort, + OutputData) + ); + IoWrite32 ( + (UINTN) OutputPort, + (UINT32) (OutputData) + ); + + OutputPort = R_PCH_IO_APM_STS; + OutputData = Data; + + // + // Write data to APM DATA PORT + // + IoWrite8 ( + (UINTN) OutputPort, + (UINT8) (OutputData) + ); + OutputPort = R_PCH_IO_APM_CNT; + OutputData = Command; + + // + // Generate the APMC SMI + // + IoWrite8 ( + (UINTN) OutputPort, + (UINT8) (OutputData) + ); + + return EFI_SUCCESS; +} + +/** + Clear the SMI status + + + @retval EFI_SUCCESS The function completes successfully + @retval EFI_DEVICE_ERROR Something error occurred +**/ +EFI_STATUS +EFIAPI +SmmClear ( + VOID + ) +{ + EFI_STATUS Status; + UINT32 OutputData; + UINT32 OutputPort; + + Status = EFI_SUCCESS; + + // + // Clear the Power Button Override Status Bit, it gates EOS from being set. + // + OutputPort = mABase + R_ACPI_IO_PM1_STS; + OutputData = B_ACPI_IO_PM1_STS_PRBTNOR; + DEBUG ( + (DEBUG_VERBOSE, + "The PM1 Status Port at address %x will be written to %x.\n", + OutputPort, + OutputData) + ); + IoWrite16 ( + (UINTN) OutputPort, + (UINT16) (OutputData) + ); + + // + // Clear the APM SMI Status Bit + // + OutputPort = mABase + R_ACPI_IO_SMI_STS; + OutputData = B_ACPI_IO_SMI_STS_APM; + DEBUG ( + (DEBUG_VERBOSE, + "The SMI Status Port at address %x will be written to %x.\n", + OutputPort, + OutputData) + ); + IoWrite32 ( + (UINTN) OutputPort, + (UINT32) (OutputData) + ); + + // + // Set the EOS Bit + // + OutputPort = mABase + R_ACPI_IO_SMI_EN; + OutputData = IoRead32 ((UINTN) OutputPort); + OutputData |= B_ACPI_IO_SMI_EN_EOS; + DEBUG ( + (DEBUG_VERBOSE, + "The SMI Control Port at address %x will be written to %x.\n", + OutputPort, + OutputData) + ); + IoWrite32 ( + (UINTN) OutputPort, + (UINT32) (OutputData) + ); + + // + // There is no need to read EOS back and check if it is set. + // This can lead to a reading of zero if an SMI occurs right after the SMI_EN port read + // but before the data is returned to the CPU. + // SMM Dispatcher should make sure that EOS is set after all SMI sources are processed. + // + return Status; +} + +/** + This routine generates an SMI + + @param[in] This The EFI SMM Control protocol instance + @param[in, out] CommandPort The buffer contains data to the command port + @param[in, out] DataPort The buffer contains data to the data port + @param[in] Periodic Periodic or not + @param[in] ActivationInterval Interval of periodic SMI + + @retval EFI Status Describing the result of the operation + @retval EFI_INVALID_PARAMETER Some parameter value passed is not supported +**/ +EFI_STATUS +EFIAPI +Activate ( + IN CONST EFI_SMM_CONTROL2_PROTOCOL * This, + IN OUT UINT8 *CommandPort OPTIONAL, + IN OUT UINT8 *DataPort OPTIONAL, + IN BOOLEAN Periodic OPTIONAL, + IN UINTN ActivationInterval OPTIONAL + ) +{ + EFI_STATUS Status; + UINT8 Command; + UINT8 Data; + + if (Periodic) { + DEBUG ((DEBUG_WARN, "Invalid parameter\n")); + return EFI_INVALID_PARAMETER; + } + + // + // Extract the values from CommandPort and DataPort + // + if (CommandPort == NULL) { + Command = 0xFF; + } else { + Command = *CommandPort; + } + + if (DataPort == NULL) { + Data = 0x00; + } else { + Data = *DataPort; + } + + // + // Clear any pending the APM SMI + // + Status = SmmClear (); + if (EFI_ERROR (Status)) { + return Status; + } + + return SmmTrigger (Command, Data); +} + +/** + This routine clears an SMI + + @param[in] This The EFI SMM Control protocol instance + @param[in] Periodic Periodic or not + + @retval EFI Status Describing the result of the operation + @retval EFI_INVALID_PARAMETER Some parameter value passed is not supported +**/ +EFI_STATUS +EFIAPI +Deactivate ( + IN CONST EFI_SMM_CONTROL2_PROTOCOL *This, + IN BOOLEAN Periodic OPTIONAL + ) +{ + if (Periodic) { + return EFI_INVALID_PARAMETER; + } + + return SmmClear (); +} diff --git a/Silicon/Intel/AlderlakeSiliconPkg/Pch/SmmControl/RuntimeDxe/SmmControlDriver.h b/Silicon/Intel/AlderlakeSiliconPkg/Pch/SmmControl/RuntimeDxe/SmmControlDriver.h new file mode 100644 index 0000000000..a366de7d5e --- /dev/null +++ b/Silicon/Intel/AlderlakeSiliconPkg/Pch/SmmControl/RuntimeDxe/SmmControlDriver.h @@ -0,0 +1,122 @@ +/** @file + Header file for SMM Control Driver. + + Copyright (c) 2022, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ +#ifndef _SMM_CONTROL_DRIVER_H_ +#define _SMM_CONTROL_DRIVER_H_ + +#include + + +#define SMM_CONTROL_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('i', '4', 's', 'c') + +typedef struct { + UINTN Signature; + EFI_HANDLE Handle; + EFI_SMM_CONTROL2_PROTOCOL SmmControl; +} SMM_CONTROL_PRIVATE_DATA; + +#define SMM_CONTROL_PRIVATE_DATA_FROM_THIS(a) CR (a, SMM_CONTROL_PRIVATE_DATA, SmmControl, SMM_CONTROL_DEV_SIGNATURE) + +// +// Prototypes +// + +/** + SmmControl DXE RUNTIME Module Entry Point\n + - Introduction\n + The SmmControl module is a DXE RUNTIME driver that provides a standard way + for other drivers to trigger software SMIs. + + - @pre + - PCH Power Management I/O space base address has already been programmed. + If SmmControl Runtime DXE driver is run before Status Code Runtime Protocol + is installed and there is the need to use Status code in the driver, it will + be necessary to add EFI_STATUS_CODE_RUNTIME_PROTOCOL_GUID to the dependency file. + - EFI_SMM_BASE2_PROTOCOL + - Documented in the System Management Mode Core Interface Specification. + + - @result + The SmmControl driver produces the EFI_SMM_CONTROL_PROTOCOL documented in + System Management Mode Core Interface Specification. + + @param[in] ImageHandle Handle for the image of this driver + @param[in] SystemTable Pointer to the EFI System Table + + @retval EFI_STATUS Results of the installation of the SMM Control Protocol +**/ +EFI_STATUS +EFIAPI +SmmControlDriverEntryInit ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +/** + Trigger the software SMI + + @param[in] Command The value to be set on the software SMI command port + @param[in] Data The value to be set on the software SMI data port + + @retval EFI_SUCCESS Function completes successfully +**/ +EFI_STATUS +EFIAPI +SmmTrigger ( + IN UINT8 Command, + IN UINT8 Data + ); + +/** + Clear the SMI status + + + @retval EFI_SUCCESS The function completes successfully + @retval EFI_DEVICE_ERROR Something error occurred +**/ +EFI_STATUS +EFIAPI +SmmClear ( + VOID + ); + +/** + This routine generates an SMI + + @param[in] This The EFI SMM Control protocol instance + @param[in, out] ArgumentBuffer The buffer of argument + @param[in, out] ArgumentBufferSize The size of the argument buffer + @param[in] Periodic Periodic or not + @param[in] ActivationInterval Interval of periodic SMI + + @retval EFI Status Describing the result of the operation + @retval EFI_INVALID_PARAMETER Some parameter value passed is not supported +**/ +EFI_STATUS +EFIAPI +Activate ( + IN CONST EFI_SMM_CONTROL2_PROTOCOL *This, + IN OUT UINT8 *ArgumentBuffer OPTIONAL, + IN OUT UINT8 *ArgumentBufferSize OPTIONAL, + IN BOOLEAN Periodic OPTIONAL, + IN UINTN ActivationInterval OPTIONAL + ); + +/** + This routine clears an SMI + + @param[in] This The EFI SMM Control protocol instance + @param[in] Periodic Periodic or not + + @retval EFI Status Describing the result of the operation + @retval EFI_INVALID_PARAMETER Some parameter value passed is not supported +**/ +EFI_STATUS +EFIAPI +Deactivate ( + IN CONST EFI_SMM_CONTROL2_PROTOCOL *This, + IN BOOLEAN Periodic OPTIONAL + ); +#endif -- 2.36.1.windows.1