From: "Chuang, Rosen" <rosen.chuang@intel.com>
To: "Kasbekar, Saloni" <saloni.kasbekar@intel.com>,
"devel@edk2.groups.io" <devel@edk2.groups.io>
Cc: "Chaganty, Rangasai V" <rangasai.v.chaganty@intel.com>,
"Desimone, Nathaniel L" <nathaniel.l.desimone@intel.com>,
"Oram, Isaac W" <isaac.w.oram@intel.com>
Subject: Re: [edk2-devel] [PATCH v2 5/7] AlderlakeSiliconPkg/Pch: Add drivers
Date: Wed, 16 Aug 2023 02:46:04 +0000 [thread overview]
Message-ID: <PH0PR11MB56262503FB8EFFBD2D4E820B9215A@PH0PR11MB5626.namprd11.prod.outlook.com> (raw)
In-Reply-To: <09c9815c5f0382b638d24c777a4eb9105afc62e5.1691170619.git.saloni.kasbekar@intel.com>
Reviewed-by: Rosen Chuang < rosen.chuang@intel.com>
-----Original Message-----
From: Kasbekar, Saloni <saloni.kasbekar@intel.com>
Sent: Saturday, August 5, 2023 1:38 AM
To: devel@edk2.groups.io
Cc: Kasbekar, Saloni <saloni.kasbekar@intel.com>; Chaganty, Rangasai V <rangasai.v.chaganty@intel.com>; Desimone, Nathaniel L <nathaniel.l.desimone@intel.com>; Oram, Isaac W <isaac.w.oram@intel.com>; Chuang, Rosen <rosen.chuang@intel.com>
Subject: [PATCH v2 5/7] AlderlakeSiliconPkg/Pch: Add drivers
Adds the following drivers:
- PchSmiDispatcher
- SmmControl
Cc: Sai Chaganty <rangasai.v.chaganty@intel.com>
Cc: Nate DeSimone <nathaniel.l.desimone@intel.com>
Cc: Isaac Oram <isaac.w.oram@intel.com>
Cc: Rosen Chuang <rosen.chuang@intel.com>
Signed-off-by: Saloni Kasbekar <saloni.kasbekar@intel.com>
---
.../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..637b9bb437
--- /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.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+#include "PchSmmHelpers.h"
+#include <Library/SmiHandlerProfileLib.h>
+#include <Register/PchRegs.h>
+#include <Register/PmcRegs.h>
+#include <Register/TcoRegs.h>
+#include <Register/PchRegsLpc.h>
+#include <Register/SpiRegs.h>
+#include <Library/PchPciBdfLib.h>
+#include <Library/S3BootScriptLib.h>
+#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;
+}
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.<BR>
+# 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.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef _PCH_SMI_HELPER_H_
+#define _PCH_SMI_HELPER_H_
+#include <Uefi/UefiBaseType.h>
+
+/**
+ 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.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+#ifndef _PCH_SMM_H_
+#define _PCH_SMM_H_
+
+#include <Uefi.h>
+#include <Protocol/PciRootBridgeIo.h>
+#include <Protocol/LoadedImage.h>
+#include <Protocol/SmmControl2.h>
+#include <Protocol/SmmUsbDispatch2.h>
+#include <Protocol/SmmSxDispatch2.h>
+#include <Protocol/SmmSwDispatch2.h>
+#include <Protocol/SmmGpiDispatch2.h>
+#include <Protocol/SmmPowerButtonDispatch2.h>
+#include <Protocol/SmmPeriodicTimerDispatch2.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/DebugLib.h>
+#include <Library/IoLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/SmmServicesTableLib.h>
+#include <Library/ReportStatusCodeLib.h>
+#include <Library/PerformanceLib.h>
+#include <Protocol/SmmReadyToLock.h>
+#include <IndustryStandard/Pci30.h>
+#include <Library/PchCycleDecodingLib.h>
+#include <Library/PchPcrLib.h>
+#include <Library/PciSegmentLib.h>
+#include <Library/GpioLib.h>
+#include <Library/PchInfoLib.h>
+#include <Library/EspiLib.h>
+#include <Library/GpioPrivateLib.h>
+#include <Protocol/PchTcoSmiDispatch.h>
+#include <Protocol/PchPcieSmiDispatch.h>
+#include <Protocol/PchAcpiSmiDispatch.h>
+#include <Protocol/PchSmiDispatch.h>
+#include <Library/PmcLib.h>
+
+#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.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+#include "PchSmm.h"
+#include "PchSmmHelpers.h"
+#include "PchSmiHelper.h"
+#include <Library/SmiHandlerProfileLib.h>
+#include <Register/GpioRegs.h>
+#include <Register/PmcRegs.h>
+#include <Register/TcoRegs.h>
+#include <Register/RtcRegs.h>
+
+#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;
+}
+
+/**
+ <b>PchSmiDispatcher SMM Module Entry Point</b>\n
+ - <b>Introduction</b>\n
+ The PchSmiDispatcher module is an SMM driver which provides SMI handler registration
+ services for PCH generated SMIs.
+
+ - <b>Details</b>\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.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+#include "PchSmm.h"
+#include "PchSmmHelpers.h"
+#include <Library/SmiHandlerProfileLib.h>
+#include <Register/GpioRegs.h>
+#include <Register/PmcRegs.h>
+
+//
+// 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.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+#include "PchSmmHelpers.h"
+#include <Register/PmcRegs.h>
+
+///
+/// #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.<BR>
+ 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.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+#include "PchSmmHelpers.h"
+#include <Protocol/PchSmmPeriodicTimerControl.h>
+#include <Library/PmcPrivateLib.h>
+
+//
+// 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.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+#include <PchSmmHelpers.h>
+#include <Library/PmcPrivateLib.h>
+
+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.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+#include "PchSmmHelpers.h"
+#include <Protocol/SmmCpu.h>
+#include <Register/PchRegsLpc.h>
+#include <Register/PmcRegs.h>
+
+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.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+#include "PchSmmHelpers.h"
+#include <Register/PmcRegs.h>
+#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.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+#include "PchSmmHelpers.h"
+#include <Register/PmcRegs.h>
+#include <Library/PchPciBdfLib.h>
+
+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.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+#include "PchSmmHelpers.h"
+#include <Register/PchRegs.h>
+#include <Register/PmcRegs.h>
+#include <Register/TcoRegs.h>
+
+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.<BR>
+ 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.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+#include "PchSmmHelpers.h"
+#include <Register/PmcRegs.h>
+#include <Register/PchPcieRpRegs.h>
+#include <Library/CpuPcieInfoFruLib.h>
+#include <CpuPcieInfo.h>
+#include <Library/PchPcieRpLib.h>
+#include <Library/PchInfoLib.h>
+#include <Library/PchPciBdfLib.h>
+
+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.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+#include <Register/TcoRegs.h>
+
+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.<BR>
+# 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.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+#include <Library/IoLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Guid/EventGroup.h>
+#include <Library/PmcLib.h>
+#include <Library/GpioLib.h>
+#include <IndustryStandard/Pci30.h>
+#include <Register/PchRegsLpc.h>
+#include <Register/SpiRegs.h>
+#include <Register/PmcRegs.h>
+#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));
+}
+
+/**
+ <b>SmmControl DXE RUNTIME Module Entry Point</b>\n
+ - <b>Introduction</b>\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.<BR>
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+#ifndef _SMM_CONTROL_DRIVER_H_
+#define _SMM_CONTROL_DRIVER_H_
+
+#include <Protocol/SmmControl2.h>
+
+
+#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
+//
+
+/**
+ <b>SmmControl DXE RUNTIME Module Entry Point</b>\n
+ - <b>Introduction</b>\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
-=-=-=-=-=-=-=-=-=-=-=-
Groups.io Links: You receive all messages sent to this group.
View/Reply Online (#107784): https://edk2.groups.io/g/devel/message/107784
Mute This Topic: https://groups.io/mt/100551004/7686176
Group Owner: devel+owner@edk2.groups.io
Unsubscribe: https://edk2.groups.io/g/devel/unsub [rebecca@openfw.io]
-=-=-=-=-=-=-=-=-=-=-=-
next prev parent reply other threads:[~2023-08-16 2:46 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-08-04 17:37 [edk2-devel] [PATCH v2 1/7] AlderlakeSiliconPkg: Add package and library instances Saloni Kasbekar
2023-08-04 17:37 ` [edk2-devel] [PATCH v2 2/7] AlderlakeSiliconPkg: Add Cpu modules Saloni Kasbekar
2023-08-16 2:45 ` Chuang, Rosen
2023-08-04 17:37 ` [edk2-devel] [PATCH v2 3/7] AlderlakeSiliconPkg/Pch: Add include headers Saloni Kasbekar
2023-08-16 2:44 ` Chuang, Rosen
2023-08-04 17:37 ` [edk2-devel] [PATCH v2 4/7] AlderlakeSiliconPkg/Pch: Add libraries Saloni Kasbekar
2023-08-16 2:42 ` Chuang, Rosen
2023-08-04 17:37 ` [edk2-devel] [PATCH v2 5/7] AlderlakeSiliconPkg/Pch: Add drivers Saloni Kasbekar
2023-08-16 2:46 ` Chuang, Rosen [this message]
2023-08-04 17:37 ` [edk2-devel] [PATCH v2 6/7] AlderlakeSiliconPkg/SystemAgent: Add include headers Saloni Kasbekar
2023-08-16 2:45 ` Chuang, Rosen
2023-08-04 17:37 ` [edk2-devel] [PATCH v2 7/7] AlderlakeSiliconPkg/SystemAgent: Add library and driver modules Saloni Kasbekar
2023-08-16 2:45 ` Chuang, Rosen
2023-08-16 2:42 ` [edk2-devel] [PATCH v2 1/7] AlderlakeSiliconPkg: Add package and library instances Chuang, Rosen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-list from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=PH0PR11MB56262503FB8EFFBD2D4E820B9215A@PH0PR11MB5626.namprd11.prod.outlook.com \
--to=devel@edk2.groups.io \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox