From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: mx.groups.io; dkim=missing; spf=pass (domain: intel.com, ip: 192.55.52.136, mailfrom: nathaniel.l.desimone@intel.com) Received: from mga12.intel.com (mga12.intel.com [192.55.52.136]) by groups.io with SMTP; Fri, 16 Aug 2019 17:53:51 -0700 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga008.jf.intel.com ([10.7.209.65]) by fmsmga106.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 16 Aug 2019 17:53:49 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.64,395,1559545200"; d="scan'208";a="171573501" Received: from orsmsx106.amr.corp.intel.com ([10.22.225.133]) by orsmga008.jf.intel.com with ESMTP; 16 Aug 2019 17:53:49 -0700 Received: from orsmsx111.amr.corp.intel.com (10.22.240.12) by ORSMSX106.amr.corp.intel.com (10.22.225.133) with Microsoft SMTP Server (TLS) id 14.3.439.0; Fri, 16 Aug 2019 17:53:49 -0700 Received: from orsmsx114.amr.corp.intel.com ([169.254.8.96]) by ORSMSX111.amr.corp.intel.com ([169.254.12.226]) with mapi id 14.03.0439.000; Fri, 16 Aug 2019 17:53:48 -0700 From: "Nate DeSimone" To: "Kubacki, Michael A" , "devel@edk2.groups.io" CC: "Chaganty, Rangasai V" , "Chiu, Chasel" , "Gao, Liming" , "Kinney, Michael D" , "Sinha, Ankit" Subject: Re: [edk2-platforms][PATCH V1 27/37] CoffeelakeSiliconPkg/Pch: Add PchSmiDispatcher Thread-Topic: [edk2-platforms][PATCH V1 27/37] CoffeelakeSiliconPkg/Pch: Add PchSmiDispatcher Thread-Index: AQHVVJEabhJ3Al+pqkueDhT8AvpzT6b+gzlA Date: Sat, 17 Aug 2019 00:53:48 +0000 Message-ID: <02A34F284D1DA44BB705E61F7180EF0AAEE12A9C@ORSMSX114.amr.corp.intel.com> References: <20190817001603.30632-1-michael.a.kubacki@intel.com> <20190817001603.30632-28-michael.a.kubacki@intel.com> In-Reply-To: <20190817001603.30632-28-michael.a.kubacki@intel.com> Accept-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: dlp-product: dlpe-windows dlp-version: 11.2.0.6 dlp-reaction: no-action x-titus-metadata-40: eyJDYXRlZ29yeUxhYmVscyI6IiIsIk1ldGFkYXRhIjp7Im5zIjoiaHR0cDpcL1wvd3d3LnRpdHVzLmNvbVwvbnNcL0ludGVsMyIsImlkIjoiNTdhMmVmYjYtZjBjZC00YzU1LTk5YTUtYjBkNTE0ZWZmNjIxIiwicHJvcHMiOlt7Im4iOiJDVFBDbGFzc2lmaWNhdGlvbiIsInZhbHMiOlt7InZhbHVlIjoiQ1RQX05UIn1dfV19LCJTdWJqZWN0TGFiZWxzIjpbXSwiVE1DVmVyc2lvbiI6IjE3LjEwLjE4MDQuNDkiLCJUcnVzdGVkTGFiZWxIYXNoIjoieWJrbG5TbjU2RnNyM1p4VTVKZitNTUh2MFhCTDJPelZrZjdIcnd1TDZWbGhtNlM2cXZLaXc3S0FNRXRwQ2lJMyJ9 x-ctpclassification: CTP_NT x-originating-ip: [10.22.254.139] MIME-Version: 1.0 Return-Path: nathaniel.l.desimone@intel.com Content-Language: en-US Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable Reviewed-by: Nate DeSimone -----Original Message----- From: Kubacki, Michael A=20 Sent: Friday, August 16, 2019 5:16 PM To: devel@edk2.groups.io Cc: Chaganty, Rangasai V ; Chiu, Chasel ; Desimone, Nathaniel L = ; Gao, Liming ; Kinney, Michael D ; Sinha, Ankit Subject: [edk2-platforms][PATCH V1 27/37] CoffeelakeSiliconPkg/Pch: Add Pch= SmiDispatcher REF:https://bugzilla.tianocore.org/show_bug.cgi?id=3D2082 Adds the PchSmiDispatcher module. Dispatches PCH SMIs to appropriate SMI handlers registered in various SMM modules. Cc: Sai Chaganty Cc: Chasel Chiu Cc: Nate DeSimone Cc: Liming Gao Cc: Michael D Kinney Cc: Ankit Sinha Signed-off-by: Michael Kubacki --- Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmiDispatch= er.inf | 109 + Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/IoTrap.h = | 228 ++ Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmm.h = | 1031 ++++++++ Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmEspi.h = | 342 +++ Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmHelpers.= h | 157 ++ Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchxSmmHelpers= .h | 105 + Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/IoTrap.c = | 1264 ++++++++++ Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmiDispatch= .c | 2452 ++++++++++++++++++++ Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmCore.c = | 911 ++++++++ Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmEspi.c = | 1595 +++++++++++++ Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmGpi.c = | 254 ++ Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmHelpers.= c | 358 +++ Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmPeriodic= Timer.c | 675 ++++++ Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmPowerBut= ton.c | 83 + Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmSw.c = | 385 +++ Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmSx.c = | 229 ++ Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmUsb.c = | 231 ++ Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchxSmmHelpers= .c | 764 ++++++ 18 files changed, 11173 insertions(+) diff --git a/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/Pc= hSmiDispatcher.inf b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatche= r/Smm/PchSmiDispatcher.inf new file mode 100644 index 0000000000..38d5dbeebf --- /dev/null +++ b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmiDis= patcher.inf @@ -0,0 +1,109 @@ +## @file +# Component description file for the Pch SMI Dispatch Handlers module +# +# Copyright (c) 2019 Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] +INF_VERSION =3D 0x00010017 +BASE_NAME =3D PchSmiDispatcher +FILE_GUID =3D B0D6ED53-B844-43f5-BD2F-61095264E77E +VERSION_STRING =3D 1.0 +MODULE_TYPE =3D DXE_SMM_DRIVER +PI_SPECIFICATION_VERSION =3D 1.10 +ENTRY_POINT =3D InitializePchSmmDispatcher + + +[LibraryClasses] +UefiBootServicesTableLib +UefiDriverEntryPoint +IoLib +DebugLib +PcdLib +BaseLib +BaseMemoryLib +HobLib +DevicePathLib +PchCycleDecodingLib +PchPcieRpLib +PchPcrLib +SmmServicesTableLib +ReportStatusCodeLib +PerformanceLib +DxeServicesTableLib +GpioLib +GpioPrivateLib +PchEspiLib +S3BootScriptLib +ConfigBlockLib +PmcPrivateLib +PmcLib +SmiHandlerProfileLib + + +[Packages] +MdePkg/MdePkg.dec +CoffeelakeSiliconPkg/SiPkg.dec + + +[Pcd] +# Progress Code for S3 Suspend end. +# PROGRESS_CODE_S3_SUSPEND_END =3D (EFI_SOFTWARE_SMM_DRIVER | (EFI_OEM_S= PECIFIC | 0x00000001)) =3D 0x03078001 +gSiPkgTokenSpaceGuid.PcdProgressCodeS3SuspendEnd +gSiPkgTokenSpaceGuid.PcdEfiGcdAllocateType + + +[Sources] +PchSmm.h +PchSmmCore.c +PchSmmHelpers.h +PchSmmHelpers.c +PchxSmmHelpers.h +PchxSmmHelpers.c +PchSmmUsb.c +PchSmmGpi.c +PchSmmPowerButton.c +PchSmmSw.c +PchSmmSx.c +PchSmmPeriodicTimer.c +IoTrap.c +PchSmiDispatch.c +PchSmmEspi.c + + +[Protocols] +gEfiPciRootBridgeIoProtocolGuid ## CONSUMES +gEfiSmmGpiDispatch2ProtocolGuid ## PRODUCES +gEfiSmmSxDispatch2ProtocolGuid ## PRODUCES +gEfiSmmSwDispatch2ProtocolGuid ## PRODUCES +gEfiSmmUsbDispatch2ProtocolGuid ## PRODUCES +gEfiSmmPowerButtonDispatch2ProtocolGuid ## PRODUCES +gEfiSmmPeriodicTimerDispatch2ProtocolGuid ## PRODUCES +gEfiSmmBase2ProtocolGuid ## CONSUMES +gEfiSmmCpuProtocolGuid ## CONSUMES +gEfiSmmReadyToLockProtocolGuid ## CONSUMES +gEfiSmmIoTrapDispatch2ProtocolGuid ## PRODUCES +gPchSmmIoTrapControlGuid ## PRODUCES +gPchTcoSmiDispatchProtocolGuid ## PRODUCES +gPchPcieSmiDispatchProtocolGuid ## PRODUCES +gPchAcpiSmiDispatchProtocolGuid ## PRODUCES +gPchSmiDispatchProtocolGuid ## PRODUCES +gPchEspiSmiDispatchProtocolGuid ## PRODUCES +gPchSmmPeriodicTimerControlGuid ## PRODUCES +gIoTrapExDispatchProtocolGuid ## PRODUCES +gPchNvsAreaProtocolGuid ## CONSUMES + + +[Guids] + + +[Depex] +gEfiPciRootBridgeIoProtocolGuid AND +gEfiPciHostBridgeResourceAllocationProtocolGuid AND ## This is to ensure t= hat PCI MMIO resource has been prepared and available for this driver to al= locate. +gEfiSmmCpuProtocolGuid AND +gEfiSmmBase2ProtocolGuid AND ## This is for SmmServicesTableLib +gPchNvsAreaProtocolGuid + diff --git a/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/Io= Trap.h b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/IoTrap= .h new file mode 100644 index 0000000000..9d6a459ff3 --- /dev/null +++ b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/IoTrap.h @@ -0,0 +1,228 @@ +/** @file + Defines and prototypes for the IoTrap SMM driver + + Copyright (c) 2019 Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef _IO_TRAP_H_ +#define _IO_TRAP_H_ + +// +// Include files +// +#include +#include +#include +#include +#include + +#define IO_TRAP_HANDLER_NUM 4 + +// +// Driver private data +// +#define IO_TRAP_INSTANCE_SIGNATURE SIGNATURE_32 ('I', 'O', 'T', 'P') + +typedef struct { + EFI_HANDLE IoTrapHandle; + /** + The callback linked list for all "merged" IoTrap callbacks. + **/ + LIST_ENTRY CallbackDataBase; + /** + The IoTrap IO range used length tracking for "merged" IoTrap register. + **/ + UINT32 TrapUsedLength; + /** + Determine if IoTrap can be merged with other IoTrap callbacks. + If MergeDisable is TRUE, then there is only one callback function for = one IoTrap register. + If MergeDisable is FALSE, then there are multiple callbacks in the "Ca= llbackDataBase" for one IoTrap register. + **/ + BOOLEAN MergeDisable; + /** + Indicator of the resource tracking in ACPI. + If the registration address is not 0, it's caller's responsibility to = reserve the IO resource in ACPI. + **/ + BOOLEAN ReservedAcpiIoResource; + /** + Dispatcher for each IoTrap register. + **/ + PCH_SMI_DISPATCH_CALLBACK CallbackDispatcher; +} IO_TRAP_ENTRY_ATTRIBUTES; + +typedef struct { + UINT32 Signature; + EFI_HANDLE Handle; + EFI_SMM_IO_TRAP_DISPATCH2_PROTOCOL EfiSmmIoTrapDispatchProtocol; + PCH_SMM_IO_TRAP_CONTROL_PROTOCOL PchSmmIoTrapControlProtocol; = ///< Protocol for runtime control the IoTrap state + IO_TRAP_EX_DISPATCH_PROTOCOL IoTrapExDispatchProtocol; = ///< Protocol for IoTrap Extension + IO_TRAP_ENTRY_ATTRIBUTES Entry[IO_TRAP_HANDLER_NUM]; +} IO_TRAP_INSTANCE; + +#define IO_TRAP_INSTANCE_FROM_THIS(a) CR (a, IO_TRAP_INSTANCE, EfiSmmIoTra= pDispatchProtocol, IO_TRAP_INSTANCE_SIGNATURE) + +/// +/// "IOTRAP" RECORD +/// Linked list data structures +/// +#define IO_TRAP_RECORD_SIGNATURE SIGNATURE_32 ('I', 'T', 'R', 'C') + +typedef struct _IO_TRAP_RECORD { + UINT32 Signature; + LIST_ENTRY Link; + IO_TRAP_EX_REGISTER_CONTEXT Context; + /** + The callback function of IoTrap protocol. + This also indicate it's the record for IoTrapProtocol. + Only one of IoTrapCallback or IoTrapExCallback is valid at a time. + **/ + EFI_SMM_HANDLER_ENTRY_POINT2 IoTrapCallback; + /** + The callback function of IoTrapEx protocol + This also indicate it's the record for IoTrapExProtocol. + Only one of IoTrapCallback or IoTrapExCallback is valid at a time. + **/ + IO_TRAP_EX_DISPATCH_CALLBACK IoTrapExCallback; + UINT8 IoTrapNumber; +} IO_TRAP_RECORD; + +#define IO_TRAP_RECORD_FROM_LINK(_record) CR (_record, IO_TRAP_RECORD, Lin= k, IO_TRAP_RECORD_SIGNATURE) + +// +// Prototypes +// +/** + The IoTrap module abstracts PCH I/O trapping capabilities for other driv= ers. + This driver manages the limited I/O trap resources. + + @param[in] ImageHandle Image handle for this driver image + + @retval EFI_SUCCESS Driver initialization completed su= ccessfully +**/ +EFI_STATUS +EFIAPI +InstallIoTrap ( + IN EFI_HANDLE ImageHandle + ); + +/** + Register a new IO Trap SMI dispatch function with a parent SMM driver. + The caller will provide information about the IO trap characteristics vi= a + the context. This includes base address, length, read vs. r/w, etc. + This function will autoallocate IO base address from a common pool if th= e base address is 0, + and the RegisterContext Address field will be updated. + The service will not perform GCD allocation if the base address is non-z= ero. + In this case, the caller is responsible for the existence and allocation= of the + specific IO range. + This function looks for the suitable handler and Register a new IoTrap h= andler + if the IO Trap handler is not used. It also enable the IO Trap Range to = generate + SMI. + + @param[in] This Pointer to the EFI_SMM_IO_TRAP_DISPATCH= 2_PROTOCOL instance. + @param[in] DispatchFunction Pointer to dispatch function to be invok= ed for + this SMI source. + @param[in, out] RegisterContext Pointer to the dispatch function's conte= xt. + The caller fills this context in before = calling + the register function to indicate to the= register + function the IO trap SMI source for whic= h the dispatch + function should be invoked. This may no= t be NULL. + If the registration address is not 0, it= 's caller's responsibility + to reserve the IO resource in ACPI. + @param[out] DispatchHandle Handle of dispatch function, for when in= terfacing + with the parent SMM driver, will be the = address of linked + list link in the call back record. This= may not be NULL. + + @retval EFI_SUCCESS The dispatch function has been successfu= lly + registered and the SMI source has been e= nabled. + @retval EFI_DEVICE_ERROR The driver was unable to enable the SMI = source. + @retval EFI_OUT_OF_RESOURCES Insufficient resources are available + @retval EFI_INVALID_PARAMETER Address requested is already in use. + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLo= ck event has been triggered +**/ +EFI_STATUS +EFIAPI +IoTrapRegister ( + IN CONST EFI_SMM_IO_TRAP_DISPATCH2_PROTOCOL *This, + IN EFI_SMM_HANDLER_ENTRY_POINT2 DispatchFunction, + IN OUT EFI_SMM_IO_TRAP_REGISTER_CONTEXT *RegisterContext, + OUT EFI_HANDLE *DispatchHandle + ); + +/** + Unregister a child SMI source dispatch function with a parent SMM driver= . + + @param[in] This Pointer to the EFI_SMM_IO_TRAP_DISPATCH= 2_PROTOCOL instance. + @param[in] DispatchHandle Handle of dispatch function to deregiste= r. + + @retval EFI_SUCCESS The dispatch function has been successfu= lly + unregistered and the SMI source has been= disabled + if there are no other registered child d= ispatch + functions for this SMI source. + @retval EFI_INVALID_PARAMETER Handle is invalid. + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLo= ck event has been triggered +**/ +EFI_STATUS +EFIAPI +IoTrapUnRegister ( + IN CONST EFI_SMM_IO_TRAP_DISPATCH2_PROTOCOL *This, + IN EFI_HANDLE DispatchHandle + ); + +/** + This I/O Trap SMI handler invokes the ACPI reference code to handle the = SMI. + It currently assumes it owns all of the IO trap SMI. + + @param[in] DispatchHandle Not used + +**/ +VOID +EFIAPI +IoTrapCallback ( + IN EFI_HANDLE DispatchHandle + ); + +/** + Pause IoTrap callback function. + + This function disables the SMI enable of IoTrap according to the Dispatc= hHandle, + which is returned by IoTrap callback registration. It only supports the = DispatchHandle + with MergeDisable TRUE and address not zero. + + @param[in] This Pointer to the PCH_SMM_IO_TRAP_CONTROL_P= ROTOCOL instance. + @param[in] DispatchHandle Handle of the child service to change st= ate. + + @retval EFI_SUCCESS This operation is complete. + @retval EFI_INVALID_PARAMETER The DispatchHandle is invalid. + @retval EFI_ACCESS_DENIED The SMI status is alrady PAUSED. +**/ +EFI_STATUS +EFIAPI +IoTrapControlPause ( + IN PCH_SMM_IO_TRAP_CONTROL_PROTOCOL * This, + IN EFI_HANDLE DispatchHandle + ); + +/** + Resume IoTrap callback function. + + This function enables the SMI enable of IoTrap according to the Dispatch= Handle, + which is returned by IoTrap callback registration. It only supports the = DispatchHandle + with MergeDisable TRUE and address not zero. + + @param[in] This Pointer to the PCH_SMM_IO_TRAP_CONTROL_P= ROTOCOL instance. + @param[in] DispatchHandle Handle of the child service to change st= ate. + + @retval EFI_SUCCESS This operation is complete. + @retval EFI_INVALID_PARAMETER The DispatchHandle is invalid. + @retval EFI_ACCESS_DENIED The SMI status is alrady RESUMED. +**/ +EFI_STATUS +EFIAPI +IoTrapControlResume ( + IN PCH_SMM_IO_TRAP_CONTROL_PROTOCOL * This, + IN EFI_HANDLE DispatchHandle + ); + +#endif diff --git a/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/Pc= hSmm.h b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmm= .h new file mode 100644 index 0000000000..1906e32b5a --- /dev/null +++ b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmm.h @@ -0,0 +1,1031 @@ +/** @file + Prototypes and defines for the PCH SMM Dispatcher. + + Copyright (c) 2019 Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef _PCH_SMM_H_ +#define _PCH_SMM_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "IoTrap.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 ot= her types +/// of addresses. To address Will's concern, I think it prudent to accomm= odate 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 =3D -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 doe= sn'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 o= r in case +// this changes. This is a good idea because PCI_ADDR will change, for ex= ample, 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 =3D=3D PCH_SMM_ADD= R_TYPE_NULL) ///< "returns" true when BitDesc is NULL +#define NULL_THIS_BIT_DESC(BitDesc) ((BitDesc).Reg.Type =3D PCH_SMM_ADDR_T= YPE_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 s= tatus bit for the SMI event. Might be the same as TopLevelSmi + PCH_SMM_BIT_DESC PmcSmiSts; ///< Refereing to the top leve= l 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 \ + } + +/// +/// CHILD CONTEXTS +/// To keep consistent w/ the architecture, we'll need to provide the cont= ext +/// to the child when we call its callback function. After talking with W= ill, +/// we agreed that we'll need functions to "dig" the context out of the ha= rdware +/// in many cases (Sx, Trap, Gpi, etc), and we'll need a function to compa= re those +/// contexts to prevent unnecessary dispatches. I'd like a general type f= or these +/// "GetContext" functions, so I'll need a union of all the protocol conte= xts 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_CONTEXT; + +/// +/// Misc data for PchDispatcher usage. +/// For PeriodicTimer, since the ElapsedTime is removed from EFI_SMM_PERIO= DIC_TIMER_REGISTER_CONTEXT of EDKII, +/// and PchDispatcher needs it for every record. Thus move it here to supp= ort ElapsedTime. +/// +typedef union { + UINTN ElapsedTime; +} 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 po= ssible. +/// Assumption: We don't need to pass in an enumeration for the protocol b= ecause 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 CommBufferSi= ze 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 Cont= ext" 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_M= AX]; + +/// +/// MAPPING CONTEXT TO BIT DESCRIPTIONS +/// I'd like to have a general approach to mapping contexts to bit descrip= tions. +/// Sometimes, we'll find that we can use table lookups or constant assign= ments; +/// other times, we'll find that we'll need to use a function to perform t= he 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 g= et rid +/// of the macros and just use function calls or variable assignments. Do= esn'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 b= e 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 inst= ances + @param[out] SrcDesc The pointer to the source description + +**/ +VOID +MapPeriodicTimerToSrcDesc ( + IN PCH_SMM_CONTEXT *DispatchCon= text, + 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 invok= ed for this SMI source. + @param[in] DispatchContext Pointer to the dispatch function's conte= xt. + @param[out] DispatchHandle Handle of dispatch function, for when in= terfacing + 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 databas= e record + @retval EFI_INVALID_PARAMETER The input parameter is invalid + @retval EFI_SUCCESS The dispatch function has been successfu= lly + registered and the SMI source has been e= nabled. +**/ +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 der= egister. + + @retval EFI_SUCCESS The dispatch function has been suc= cessfully + unregistered and the SMI source ha= s been disabled + if there are no other registered c= hild dispatch + functions for this SMI source. + @retval EFI_INVALID_PARAMETER Handle is invalid. + @retval EFI_ACCESS_DENIED Return access denied if the SmmRea= dyToLock 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 deregiste= r. + + @retval EFI_SUCCESS The dispatch function has been successfu= lly + unregistered and the SMI source has been= disabled + if there are no other registered child d= ispatch + 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 reg= ister. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source descrip= tion + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database= record + @retval EFI_SUCCESS The database record is created suc= cessfully. +**/ +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 t= o 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 calle= d when the software + SMI source specified by RegisterContext->SwSmiCpuIndex is detected. On r= eturn, + DispatchHandle contains a unique handle which may be used later to unreg= ister the function + using UnRegister(). + + @param[in] This Pointer to the EFI_SMM_SW_DISPATCH2_PRO= TOCOL instance. + @param[in] DispatchFunction Function to register for handler when t= he specified software + SMI is generated. + @param[in, out] RegisterContext Pointer to the dispatch function's cont= ext. + The caller fills this context in before= calling + the register function to indicate to th= e register + function which Software SMI input value= the + dispatch function should be invoked for= . + @param[out] DispatchHandle Handle generated by the dispatcher to t= rack the + function instance. + + @retval EFI_SUCCESS The dispatch function has been successful= ly + registered and the SMI source has been en= abled. + @retval EFI_DEVICE_ERROR The SW driver was unable to enable the SM= I source. + @retval EFI_INVALID_PARAMETER RegisterContext is invalid. The SW SMI in= put 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 softwa= re 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_PROTO= COL instance. + @param[in] DispatchHandle Handle of dispatch function to deregister= . + + @retval EFI_SUCCESS The dispatch function has been successful= ly 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 structur= e +**/ +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 sta= tus 1 + @param[in] Context2 Context 2 that includes Power Button sta= tus 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 tha= t are required + to dispatch messages to children. The SrcDesc argument isn't acutally us= ed. + + @param[in] SrcDesc Pointer to the PCH_SMM_SOURCE_DESC insta= nce. +**/ +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_DI= SPATCH2_PROTOCOL instance. + @param[in, out] SmiTickInterval Pointer to pointer of the next shorter S= MI 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 su= ccessfully +**/ +EFI_STATUS +EFIAPI +InstallPchSmmPeriodicTimerControlProtocol ( + IN EFI_HANDLE Handle + ); + +/** + When we get an SMI that indicates that we are transitioning to a sleep s= tate, + we need to actually transition to that state. We do this by disabling t= he + "SMI on sleep enable" feature, which generates an SMI when the operating= system + tries to put the system to sleep, and then physically putting the system= to sleep. +**/ +VOID +PchSmmSxGoToSleep ( + VOID + ); + +/** + 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 installatio= n +**/ +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 reg= ister. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source descrip= tion + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database= record + @retval EFI_SUCCESS The database record is created suc= cessfully. +**/ +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 der= egister. + + @retval EFI_SUCCESS The dispatch function has been suc= cessfully + unregistered and the SMI source ha= s been disabled + if there are no other registered c= hild 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 PchSmiDis= patch + @param[out] DispatchHandle The callback handle + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source descripti= on + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database r= ecord + @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 der= egister. + + @retval EFI_SUCCESS The dispatch function has been suc= cessfully + unregistered and the SMI source ha= s been disabled + if there are no other registered c= hild 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 desc= ription + @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 reg= ister. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source descrip= tion + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database= record + @retval EFI_SUCCESS The database record is created suc= cessfully. +**/ +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 CONST 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 desc= ription 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 desc= ription table + +**/ +VOID +EFIAPI +PchTcoSmiClearSource ( + CONST PCH_SMM_SOURCE_DESC *SrcDesc + ); + +/** + Initialize Source descriptor structure + + @param[in] SrcDesc Pointer to the PCH SMI source desc= ription 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_PROT= OCOL 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 contex= t. + The caller fills this context in before c= alling + the register function to indicate to the = register + function the GPI(s) for which the dispatc= h function + should be invoked. + @param[out] DispatchHandle Handle generated by the dispatcher to tra= ck the + function instance. + + @retval EFI_SUCCESS The dispatch function has been successful= ly + registered and the SMI source has been en= abled. + @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 CONST 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_PRO= TOCOL instance. + @param[in] DispatchHandle Handle of dispatch function to deregiste= r. + + @retval EFI_SUCCESS The dispatch function has been successfu= lly + unregistered and the SMI source has been= disabled + if there are no other registered child d= ispatch + 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/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/Pc= hSmmEspi.h b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/Pc= hSmmEspi.h new file mode 100644 index 0000000000..193eed6bac --- /dev/null +++ b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmEsp= i.h @@ -0,0 +1,342 @@ +/** @file + eSPI SMI Dispatch header + + Copyright (c) 2019 Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef _PCH_SMM_ESPI_H_ +#define _PCH_SMM_ESPI_H_ + +#include "PchSmmHelpers.h" + +#define ESPI_SMI_INSTANCE_SIGNATURE SIGNATURE_32 ('E', 'S', 'P', 'I'= ) +#define ESPI_SMI_RECORD_SIGNATURE SIGNATURE_32 ('E', 'S', 'R', 'C'= ) + +#define ESPI_INSTANCE_FROM_THIS(_this) CR (_this, ESPI_SMI_INSTANCE, = EfiEspiSmiDispatchProtocol, ESPI_SMI_INSTANCE_SIGNATURE) +#define ESPI_RECORD_FROM_LINK(_link) CR (_link, ESPI_SMI_RECORD, Li= nk, ESPI_SMI_RECORD_SIGNATURE) + +typedef enum { + EspiBiosWrProtect, ///< BIOS Write Protect + EspiSerialIrq, ///< eSPI Master Asserted SMI + EspiPmc, ///< eSPI PMC SMI + EspiTopLevelTypeMax +} ESPI_TOP_LEVEL_TYPE; + +typedef enum { + BiosWrProtect, ///< BIOS Write Protect + BiosWrReport, ///< Peripheral Channel BIOS Write Protect + PcNonFatalErr, ///< Peripheral Channel Non Fatal Error + PcFatalErr, ///< Peripheral Channel Fatal Error + VwNonFatalErr, ///< Virtual Wire Non Fatal Error + VwFatalErr, ///< Virtual Wire Fatal Error + FlashNonFatalErr, ///< Flash Channel Non Fatal Error + FlashFatalErr, ///< Flash Channel Fatal Error + LnkType1Err, ///< Link Error + EspiSlaveSmi, ///< Espi Slave SMI + EspiSmiTypeMax +} ESPI_SMI_TYPE; + +/// +/// This is used to classify ESPI_SMI_TYPE to ESPI_TOP_LEVEL_TYPE. +/// Used during dispatching and unregistering +/// +typedef struct { + ESPI_SMI_TYPE Start; + ESPI_SMI_TYPE End; +} ESPI_SMI_TYPE_BARRIER; + +typedef struct _ESPI_SMI_INSTANCE { + /// + /// Signature associated with this instance + /// + UINT32 Signature; + /// + /// EFI_HANDLE acquired when installing PchEspiSmiDispatchProtocol + /// + EFI_HANDLE Handle; + /// + /// The protocol to register or unregister eSPI SMI callbacks + /// + PCH_ESPI_SMI_DISPATCH_PROTOCOL PchEspiSmiDispatchProtocol; + /// + /// The handle acquired when registering eSPI SMI callback to PchSmiDisp= atch + /// + EFI_HANDLE PchSmiEspiHandle[EspiTopLevelTypeMax]; + /// + /// The linked list for record database. + /// + LIST_ENTRY CallbackDataBase[EspiSmiTypeMax]; + /// + /// This is an internal counter to track the number of eSPI master event= s have been registered. + /// When unregistering, we can disable the SMI if the counter is zero + /// + UINTN EspiSmiEventCounter[EspiSmiTypeMax]; + /// + /// Instance of barrier + /// + CONST ESPI_SMI_TYPE_BARRIER Barrier[EspiTopLevelTypeMax]; +} ESPI_SMI_INSTANCE; + +typedef struct _ESPI_DESCRIPTOR { + PCH_SMM_ADDRESS Address; + UINT32 SourceIsActiveAndMask; + UINT32 SourceIsActiveValue; + UINT32 ClearStatusAndMask; + UINT32 ClearStatusOrMask; +} ESPI_DESCRIPTOR; + +/// +/// A simple record to store the callbacks associated with an eSPI SMI sou= rce +/// +typedef struct _ESPI_SMI_RECORD { + UINT32 Signature; + LIST_ENTRY Link; + PCH_ESPI_SMI_DISPATCH_CALLBACK Callback; +} ESPI_SMI_RECORD; + +/** + Installs and initialize this protocol + + @param[in] ImageHandle Not used + + @retval EFI_SUCCESS Installation succeed + @retval others Installation failed +**/ +EFI_STATUS +EFIAPI +InstallEspiSmi ( + IN EFI_HANDLE ImageHandle + ); + +/** + eSPI SMI Dispatch Protocol instance to register a BIOS Write Protect eve= nt + + @param[in] This Not used + @param[in] DispatchFunction The callback to execute + @param[out] DispatchHandle The handle for this callback registration + + @retval EFI_SUCCESS Registration succeed + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock= event has been triggered + @retval others Registration failed +**/ +EFI_STATUS +EFIAPI +BiosWrProtectRegister ( + IN PCH_ESPI_SMI_DISPATCH_PROTOCOL *This, + IN PCH_ESPI_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ); + +/** + eSPI SMI Dispatch Protocol instance to register a BIOS Write Report even= t + + @param[in] This Not used + @param[in] DispatchFunction The callback to execute + @param[out] DispatchHandle The handle for this callback registration + + @retval EFI_SUCCESS Registration succeed + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock= event has been triggered + @retval others Registration failed +**/ +EFI_STATUS +EFIAPI +BiosWrReportRegister ( + IN PCH_ESPI_SMI_DISPATCH_PROTOCOL *This, + IN PCH_ESPI_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ); + +/** + eSPI SMI Dispatch Protocol instance to register a Peripheral Channel Non= Fatal Error event + + @param[in] This Not used + @param[in] DispatchFunction The callback to execute + @param[out] DispatchHandle The handle for this callback registration + + @retval EFI_SUCCESS Registration succeed + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock= event has been triggered + @retval others Registration failed +**/ +EFI_STATUS +EFIAPI +PcNonFatalErrRegister ( + IN PCH_ESPI_SMI_DISPATCH_PROTOCOL *This, + IN PCH_ESPI_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ); + +/** + eSPI SMI Dispatch Protocol instance to register a Peripheral Channel Fat= al Error event + + @param[in] This Not used + @param[in] DispatchFunction The callback to execute + @param[out] DispatchHandle The handle for this callback registration + + @retval EFI_SUCCESS Registration succeed + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock= event has been triggered + @retval others Registration failed +**/ +EFI_STATUS +EFIAPI +PcFatalErrRegister ( + IN PCH_ESPI_SMI_DISPATCH_PROTOCOL *This, + IN PCH_ESPI_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ); + +/** + eSPI SMI Dispatch Protocol instance to register a Virtual Wire Non Fatal= Error event + + @param[in] This Not used + @param[in] DispatchFunction The callback to execute + @param[out] DispatchHandle The handle for this callback registration + + @retval EFI_SUCCESS Registration succeed + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock= event has been triggered + @retval others Registration failed +**/ +EFI_STATUS +EFIAPI +VwNonFatalErrRegister ( + IN PCH_ESPI_SMI_DISPATCH_PROTOCOL *This, + IN PCH_ESPI_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ); + +/** + eSPI SMI Dispatch Protocol instance to register a Virtual Wire Fatal Err= or event + + @param[in] This Not used + @param[in] DispatchFunction The callback to execute + @param[out] DispatchHandle The handle for this callback registration + + @retval EFI_SUCCESS Registration succeed + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock= event has been triggered + @retval others Registration failed +**/ +EFI_STATUS +EFIAPI +VwFatalErrRegister ( + IN PCH_ESPI_SMI_DISPATCH_PROTOCOL *This, + IN PCH_ESPI_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ); + +/** + eSPI SMI Dispatch Protocol instance to register a Flash Channel Non Fata= l Error event + + @param[in] This Not used + @param[in] DispatchFunction The callback to execute + @param[out] DispatchHandle The handle for this callback registration + + @retval EFI_SUCCESS Registration succeed + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock= event has been triggered + @retval others Registration failed +**/ +EFI_STATUS +EFIAPI +FlashNonFatalErrRegister ( + IN PCH_ESPI_SMI_DISPATCH_PROTOCOL *This, + IN PCH_ESPI_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ); + +/** + eSPI SMI Dispatch Protocol instance to register a Flash Channel Fatal Er= ror event + + @param[in] This Not used + @param[in] DispatchFunction The callback to execute + @param[out] DispatchHandle The handle for this callback registration + + @retval EFI_SUCCESS Registration succeed + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock= event has been triggered + @retval others Registration failed +**/ +EFI_STATUS +EFIAPI +FlashFatalErrRegister ( + IN PCH_ESPI_SMI_DISPATCH_PROTOCOL *This, + IN PCH_ESPI_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ); + +/** + eSPI SMI Dispatch Protocol instance to register a Link Error event + + @param[in] This Not used + @param[in] DispatchFunction The callback to execute + @param[out] DispatchHandle The handle for this callback registration + + @retval EFI_SUCCESS Registration succeed + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock= event has been triggered + @retval others Registration failed +**/ +EFI_STATUS +EFIAPI +LnkType1ErrRegister ( + IN PCH_ESPI_SMI_DISPATCH_PROTOCOL *This, + IN PCH_ESPI_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ); + +/** + eSPI SMI Dispatch Protocol instance to register a eSPI slave SMI + NOTE: The register function is not available when the ESPI_SMI_LOCK bit = is set. + This runtine will also lock donw ESPI_SMI_LOCK bit after registrat= ion and + prevent this handler from unregistration. + On platform that supports more than 1 device through another chip select= (SPT-H), + the SMI handler itself needs to inspect both the eSPI devices' interrupt= status registers + (implementation specific for each Slave) in order to identify and servic= e the cause. + After servicing it, it has to clear the Slaves' internal SMI# status reg= isters + + @param[in] This Not used + @param[in] DispatchFunction The callback to execute + @param[out] DispatchHandle The handle for this callback regis= tration + + @retval EFI_SUCCESS Registration succeed + @retval EFI_ACCESS_DENIED Return access denied if the SmmRea= dyToLock event has been triggered + @retval EFI_ACCESS_DENIED The ESPI_SMI_LOCK is set and regis= ter is blocked. + @retval others Registration failed +**/ +EFI_STATUS +EFIAPI +EspiSlaveSmiRegister ( + IN PCH_ESPI_SMI_DISPATCH_PROTOCOL *This, + IN PCH_ESPI_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ); + +/** + eSPI SMI Dispatch Protocol instance to unregister a callback based on ha= ndle + + @param[in] This Not used + @param[in] DispatchHandle Handle acquired during registration + + @retval EFI_SUCCESS Unregister successful + @retval EFI_INVALID_PARAMETER DispatchHandle is null + @retval EFI_INVALID_PARAMETER DispatchHandle's forward link has ba= d pointer + @retval EFI_INVALID_PARAMETER DispatchHandle does not exist in dat= abase + @retval EFI_ACCESS_DENIED Unregistration is done after end of = DXE + @retval EFI_ACCESS_DENIED DispatchHandle is not allowed to unr= egistered +**/ +EFI_STATUS +EFIAPI +EspiSmiUnRegister ( + IN PCH_ESPI_SMI_DISPATCH_PROTOCOL *This, + IN EFI_HANDLE DispatchHandle + ); + +/** + eSPI SMI handler for Fatal Error recovery flow + + @param[in] DispatchHandle Handle acquired during registration +**/ +VOID +EspiDefaultFatalErrorHandler ( + VOID + ); + + +#endif diff --git a/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/Pc= hSmmHelpers.h b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm= /PchSmmHelpers.h new file mode 100644 index 0000000000..24e0975025 --- /dev/null +++ b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmHel= pers.h @@ -0,0 +1,157 @@ +/** @file + Helper functions for PCH SMM + + Copyright (c) 2019 Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef PCH_SMM_HELPERS_H +#define PCH_SMM_HELPERS_H + +#include "PchSmm.h" +#include "PchxSmmHelpers.h" +// +// ///////////////////////////////////////////////////////////////////////= //////////////////////////////////////////// +// SUPPORT / HELPER FUNCTIONS (PCH version-independent) +// + +/** + Publish SMI Dispatch protocols. + + +**/ +VOID +PchSmmPublishDispatchProtocols ( + VOID + ); + +/** + Compare 2 SMM source descriptors' enable settings. + + @param[in] Src1 Pointer to the PCH SMI source descriptio= n table 1 + @param[in] Src2 Pointer to the PCH SMI source descriptio= n 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 n= ull address type. + + @param[in] BitDesc Pointer to the PCH SMI bit descriptor + @param[in] Src Pointer to the PCH SMI source descriptio= n table 2 + + @retval TRUE The bit desc is equal to any of the enab= les 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 descriptio= n table 1 + @param[in] Src2 Pointer to the PCH SMI source descriptio= n table 2 + + @retval TRUE The statuses of the 2 SMM source descrip= tors are identical. + @retval FALSE The statuses of the 2 SMM source descrip= tors 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 se= ttings of them. + + @param[in] Src1 Pointer to the PCH SMI source descriptio= n table 1 + @param[in] Src2 Pointer to the PCH SMI source descriptio= n table 2 + + @retval TRUE The 2 SMM source descriptors are identic= al. + @retval FALSE The 2 SMM source descriptors are not ide= ntical. +**/ +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 descriptio= n 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 wou= ld also clear SMI + status bit to make initial state is correct + + @param[in] SrcDesc Pointer to the PCH SMI source descriptio= n 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 descriptio= n 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 descriptio= n 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 descriptio= n table + +**/ +VOID +PchSmmClearSourceAndBlock ( + CONST PCH_SMM_SOURCE_DESC *SrcDesc + ); + +#endif diff --git a/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/Pc= hxSmmHelpers.h b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Sm= m/PchxSmmHelpers.h new file mode 100644 index 0000000000..ba7ad42c9d --- /dev/null +++ b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchxSmmHe= lpers.h @@ -0,0 +1,105 @@ +/** @file + This driver is responsible for the registration of child drivers + and the abstraction of the PCH SMI sources. + + Copyright (c) 2019 Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef _PCHX_SMM_HELPERS_H_ +#define _PCHX_SMM_HELPERS_H_ + +#include "PchSmm.h" + +/** + Initialize bits that aren't necessarily related to an SMI source. + + + @retval EFI_SUCCESS SMI source initialization completed. + @retval Asserts Global Smi Bit is not enabled successful= ly. +**/ +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. +**/ +VOID +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 addres= s, 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 addres= s, 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 writ= e clear + +**/ +VOID +WriteBitDesc ( + CONST PCH_SMM_BIT_DESC *BitDesc, + CONST BOOLEAN ValueToWrite, + CONST BOOLEAN WriteClear + ); + +#endif diff --git a/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/Io= Trap.c b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/IoTrap= .c new file mode 100644 index 0000000000..ddab2fc378 --- /dev/null +++ b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/IoTrap.c @@ -0,0 +1,1264 @@ +/** @file + Main implementation source file for the Io Trap SMM driver + + Copyright (c) 2019 Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include "PchSmmHelpers.h" +#include +#include +#include +#include +#include + +#define GENERIC_IOTRAP_SIZE 0x100 + +// +// Module global variables +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_HANDLE mDriverImage= Handle; +GLOBAL_REMOVE_IF_UNREFERENCED EFI_HANDLE mIoTrapHandl= e; + +GLOBAL_REMOVE_IF_UNREFERENCED IO_TRAP_INSTANCE mIoTrapData; +GLOBAL_REMOVE_IF_UNREFERENCED IO_TRAP_RECORD *mIoTrapReco= rd; +GLOBAL_REMOVE_IF_UNREFERENCED PCH_NVS_AREA *mPchNvsArea= ; + + +static CONST UINT16 mLengthTable[10] =3D { 1, 2, 3, 4, 8, 16, = 32, 64, 128, 256 }; + +/** + Helper function that encapsulates IoTrap register access. + IO trap related register updates must be made in 2 registers, IOTRAP and= DMI source decode. + + @param[in] TrapHandlerNum trap number (0-3) + @param[in] Value value to be written in both registers + @param[in] SaveToBootscript if true, this register write will be saved = to bootscript + +**/ +VOID +SetIoTrapLowDword ( + IN UINT8 TrapHandlerNum, + IN UINT32 Value, + IN BOOLEAN SaveToBootscript + ) +{ + UINT32 BitMask; + UINT32 BitValue; + // + // To provide sequentially consistent programming model for IO trap + // all pending IO cycles must be flushed before enabling and before disa= bling a trap. + // Without this the trap may trigger due to IO cycle issued before the t= rap is enabled or a cycle issued before the trap is disabled might be misse= d. + // a. Issues a MemRd to PSTH IO Trap Enable bit -> This serves to flush = all previous IO cycles. + // b. Then only issues a MemWr to PSTH IO Trap Enable =3D=3D Value + // c. Issues another MemRd to PSTH IO Trap Enable bit -> This serves to = push the MemWr to PSTH and confirmed that IO Trap is in fact enabled + // + PchPcrRead32 (PID_PSTH, R_PSTH_PCR_TRPREG0 + TrapHandlerNum * 8); + PchPcrWrite32 (PID_PSTH, R_PSTH_PCR_TRPREG0 + TrapHandlerNum * 8, Value)= ; + PchPcrRead32 (PID_PSTH, R_PSTH_PCR_TRPREG0 + TrapHandlerNum * 8); + + PchPcrWrite32 (PID_DMI, R_PCH_DMI_PCR_IOT1 + TrapHandlerNum * 8, Value); + // + // Read back DMI IOTRAP register to enforce ordering so DMI write is com= pleted before any IO reads + // from other threads which may occur after this point (after SMI exit). + // + PchPcrRead32 (PID_DMI, R_PCH_DMI_PCR_IOT1 + TrapHandlerNum * 8); + if (SaveToBootscript) { + // + // Ignore the value check of PCH_PCR_BOOT_SCRIPT_READ + // + BitMask =3D 0; + BitValue =3D 0; + + PCH_PCR_BOOT_SCRIPT_READ (S3BootScriptWidthUint32, PID_PSTH, R_PSTH_PC= R_TRPREG0 + TrapHandlerNum * 8, &BitMask, &BitValue); + PCH_PCR_BOOT_SCRIPT_WRITE (S3BootScriptWidthUint32, PID_PSTH, R_PSTH_P= CR_TRPREG0 + TrapHandlerNum * 8, 1, &Value); + PCH_PCR_BOOT_SCRIPT_READ (S3BootScriptWidthUint32, PID_PSTH, R_PSTH_PC= R_TRPREG0 + TrapHandlerNum * 8, &BitMask, &BitValue); + PCH_PCR_BOOT_SCRIPT_WRITE (S3BootScriptWidthUint32, PID_DMI, R_PCH_DMI= _PCR_IOT1 + TrapHandlerNum * 8, 1, &Value); + } +} + +/** + Helper function that encapsulates IoTrap register access. + IO trap related register updates must be made in 2 registers, IOTRAP and= DMI source decode. + + @param[in] TrapHandlerNum trap number (0-3) + @param[in] Value value to be written in both registers + @param[in] SaveToBootscript if true, this register write will be saved = to bootscript + +**/ +VOID +SetIoTrapHighDword ( + IN UINT8 TrapHandlerNum, + IN UINT32 Value, + IN BOOLEAN SaveToBootscript + ) +{ + PchPcrWrite32 (PID_PSTH, R_PSTH_PCR_TRPREG0 + TrapHandlerNum * 8 + 4, Va= lue); + PchPcrWrite32 (PID_DMI, R_PCH_DMI_PCR_IOT1 + TrapHandlerNum * 8 + 4, Val= ue); + if (SaveToBootscript) { + PCH_PCR_BOOT_SCRIPT_WRITE (S3BootScriptWidthUint32, PID_PSTH, R_PSTH_P= CR_TRPREG0 + TrapHandlerNum * 8 + 4, 1, &Value); + PCH_PCR_BOOT_SCRIPT_WRITE (S3BootScriptWidthUint32, PID_DMI, R_PCH_DMI= _PCR_IOT1 + TrapHandlerNum * 8 + 4, 1, &Value); + } +} + +/** + Clear pending IOTRAP status. + If IOTRAP status is pending and IOTRAP is disabled, then BIOS will not f= ind a match SMI source + and will not dispatch any SMI handler for it. The pending status will le= ad to SMI storm. + This prevents that IOTRAP gets triggered by pending IO cycles even after= it's disabled. + + @param[in] TrapHandlerNum trap number (0-3) + +**/ +VOID +ClearPendingIoTrapStatus ( + IN UINT8 TrapHandlerNum + ) +{ + PchPcrWrite32 (PID_PSTH, R_PSTH_PCR_TRPST, (UINT32)(1 << TrapHandlerNum)= ); +} + +/** + IO resources allocated to IO traps need to be reported to OS so that the= y don't get reused. + This function makes IO trap allocation data available to ACPI + + @param[in] TrapHandlerNum trap number (0-3) + @param[in] BaseAddress address of allocated IO resource + @param[in] Track TRUE =3D resource allocated, FALSE =3D resour= ce freed + +**/ +VOID +UpdateIoTrapAcpiResources ( + IN UINT8 TrapHandlerNum, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN BOOLEAN Track + ) +{ + + if (Track =3D=3D TRUE) { + mPchNvsArea->IoTrapAddress[TrapHandlerNum] =3D (UINT16) BaseAddress; + mPchNvsArea->IoTrapStatus[TrapHandlerNum] =3D 1; + } else { + mPchNvsArea->IoTrapStatus[TrapHandlerNum] =3D 0; + } +} + +/** + Get address from IOTRAP low dword. + + @param[in] IoTrapRegLowDword IOTRAP register low dword + + @retval Address of IOTRAP setting. +**/ +STATIC +UINT16 +AddressFromLowDword ( + UINT32 IoTrapRegLowDword + ) +{ + return (UINT16) (IoTrapRegLowDword & B_PSTH_PCR_TRPREG_AD); +} + +/** + Get length from IOTRAP low dword. + + @param[in] IoTrapRegLowDword IOTRAP register low dword + + @retval Length of IOTRAP setting. +**/ +STATIC +UINT16 +LengthFromLowDword ( + UINT32 IoTrapRegLowDword + ) +{ + return (UINT16) (((IoTrapRegLowDword >> 16) & 0xFC) + 4); +} + +/** + Get ByteEnable from IOTRAP high dword. + + @param[in] IoTrapRegHighDword IOTRAP register high dword + + @retval ByteEnable of IOTRAP setting. +**/ +STATIC +UINT8 +ByteEnableFromHighDword ( + UINT32 IoTrapRegHighDword + ) +{ + return (UINT8) (IoTrapRegHighDword & 0x0F); +} + +/** + Get ByteEnableMask from IOTRAP high dword. + + @param[in] IoTrapRegHighDword IOTRAP register high dword + + @retval ByteEnableMask of IOTRAP setting. +**/ +STATIC +UINT8 +ByteEnableMaskFromHighDword ( + UINT32 IoTrapRegHighDword + ) +{ + return (UINT8) ((IoTrapRegHighDword & 0xF0) >> 4); +} + +/** + Check the IoTrap register matches the IOTRAP EX content. + + @param[in] IoTrapRecord IOTRAP registration record structure + @param[in] IoTrapRegLowDword IOTRAP register low dword + @param[in] IoTrapRegHighDword IOTRAP register high dword + + @retval TRUE Content matched + FALSE Content mismatched +**/ +STATIC +BOOLEAN +IsIoTrapExContentMatched ( + IO_TRAP_RECORD *IoTrapRecord, + UINT32 IoTrapRegLowDword, + UINT32 IoTrapRegHighDword + ) +{ + if ((IoTrapRecord->Context.Address =3D=3D AddressFromLowDword (IoTrapReg= LowDword)) && + (IoTrapRecord->Context.Length =3D=3D LengthFromLowDword (IoTrapRegLo= wDword)) && + (IoTrapRecord->Context.ByteEnable =3D=3D ByteEnableFromHighDword (Io= TrapRegHighDword)) && + (IoTrapRecord->Context.ByteEnableMask =3D=3D ByteEnableMaskFromHighD= word (IoTrapRegHighDword))) + { + return TRUE; + } + return FALSE; +} + + +/** + The helper function for IoTrap callback dispacther + + @param[in] TrapHandlerNum trap number (0-3) +**/ +VOID +IoTrapDispatcherHelper ( + UINTN TrapHandlerNum + ) +{ + IO_TRAP_RECORD *RecordInDb; + LIST_ENTRY *LinkInDb; + EFI_SMM_IO_TRAP_REGISTER_CONTEXT CurrentIoTrapRegisterData; + EFI_SMM_IO_TRAP_CONTEXT CurrentIoTrapContextData; + UINT16 BaseAddress; + UINT16 StartAddress; + UINT16 EndAddress; + UINT32 Data32; + UINT8 ActiveHighByteEnable; + BOOLEAN ReadCycle; + UINT32 WriteData; + + if (!IsListEmpty (&(mIoTrapData.Entry[TrapHandlerNum].CallbackDataBase))= ) { + Data32 =3D PchPcrRead32 (PID_PSTH, R_PSTH_PCR_TRPC); + WriteData =3D PchPcrRead32 (PID_PSTH, R_PSTH_PCR_TRPD); + + BaseAddress =3D (UINT16) (Data32 & B_PSTH_PCR_TRPC_IOA); + ActiveHighByteEnable =3D (UINT8)((Data32 & B_PSTH_PCR_TRPC_AHBE) >> 1= 6); + ReadCycle =3D (BOOLEAN) ((Data32 & B_PSTH_PCR_TRPC_RW) =3D= =3D B_PSTH_PCR_TRPC_RW); + // + // StartAddress and EndAddress will be equal if it's byte access + // + EndAddress =3D (UINT16) (HighBitSet32 ((UINT32) (ActiveHighByteEnab= le))) + BaseAddress; + StartAddress =3D (UINT16) (LowBitSet32 ((UINT32) (ActiveHighByteEnabl= e))) + BaseAddress; + + CurrentIoTrapRegisterData.Type =3D (EFI_SMM_IO_TRAP_DISPATCH_TYPE)Read= Cycle; + CurrentIoTrapContextData.WriteData =3D WriteData; + + LinkInDb =3D GetFirstNode (&(mIoTrapData.Entry[TrapHandlerNum].Callbac= kDataBase)); + + while (!IsNull (&(mIoTrapData.Entry[TrapHandlerNum].CallbackDataBase),= LinkInDb)) { + RecordInDb =3D IO_TRAP_RECORD_FROM_LINK (LinkInDb); + + // + // If MergeDisable is TRUE, no need to check the address range, disp= atch the callback function directly. + // + if (mIoTrapData.Entry[TrapHandlerNum].MergeDisable) { + if (RecordInDb->IoTrapCallback !=3D NULL) { + RecordInDb->IoTrapCallback (&RecordInDb->Link, &CurrentIoTrapCon= textData, NULL, NULL); + } + if (RecordInDb->IoTrapExCallback !=3D NULL) { + RecordInDb->IoTrapExCallback (BaseAddress, ActiveHighByteEnable,= !ReadCycle, WriteData); + } + // + // Expect only one callback available. So break immediately. + // + break; + // + // If MergeDisable is FALSE, check the address range and trap type. + // + } else { + if ((RecordInDb->Context.Address <=3D StartAddress) && + (RecordInDb->Context.Address + RecordInDb->Context.Length > En= dAddress)) { + if ((RecordInDb->Context.Type =3D=3D IoTrapExTypeReadWrite) || (= RecordInDb->Context.Type =3D=3D (IO_TRAP_EX_DISPATCH_TYPE) CurrentIoTrapReg= isterData.Type)) { + // + // Pass the IO trap context information + // + RecordInDb->IoTrapCallback (&RecordInDb->Link, &CurrentIoTrapC= ontextData, NULL, NULL); + } + // + // Break if the address is match + // + break; + } else { + LinkInDb =3D GetNextNode (&(mIoTrapData.Entry[TrapHandlerNum].Ca= llbackDataBase), &RecordInDb->Link); + if (IsNull (&(mIoTrapData.Entry[TrapHandlerNum].CallbackDataBase= ), LinkInDb)) { + // + // An IO access was trapped that does not have a handler regis= tered. + // This indicates an error condition. + // + ASSERT (FALSE); + } + } + } // end of if else block + } // end of while loop + } // end of if else block +} + +/** + IoTrap dispatcher for IoTrap register 0. + + @param[in] DispatchHandle Handle of dispatch function +**/ +VOID +EFIAPI +IoTrapDispatcher0 ( + IN EFI_HANDLE DispatchHandle + ) +{ + IoTrapDispatcherHelper (0); +} + +/** + IoTrap dispatcher for IoTrap register 1. + + @param[in] DispatchHandle Handle of dispatch function +**/ +VOID +EFIAPI +IoTrapDispatcher1 ( + IN EFI_HANDLE DispatchHandle + ) +{ + IoTrapDispatcherHelper (1); +} + +/** + IoTrap dispatcher for IoTrap register 2. + + @param[in] DispatchHandle Handle of dispatch function +**/ +VOID +EFIAPI +IoTrapDispatcher2 ( + IN EFI_HANDLE DispatchHandle + ) +{ + IoTrapDispatcherHelper (2); +} + +/** + IoTrap dispatcher for IoTrap register 3. + + @param[in] DispatchHandle Handle of dispatch function +**/ +VOID +EFIAPI +IoTrapDispatcher3 ( + IN EFI_HANDLE DispatchHandle + ) +{ + IoTrapDispatcherHelper (3); +} + +/** + IoTrap registratrion helper fucntion. + + @param[in] DispatchHandle Handle of dispatch function + @param[in] IoTrapDispatchFunction Dispatch function of IoTrapDispatc= h2Protocol. + This could be NULL if it's not fro= m IoTrapDispatch2Protocol. + @param[in] IoTrapExDispatchFunction Dispatch function of IoTrapExDispa= tchProtocol. + This could be NULL if it's not fro= m IoTrapExDispatchProtocol. + @param[in out] Address The pointer of IO Address. + If the input Addres is 0, it will = return the address assigned + by registration to this caller. + @param[in] Length Length of IO address range. + @param[in] Type Read/Write type of IO trap. + @param[in] ByteEnable Bitmap to enable trap for each byt= e of every dword alignment address. + @param[in] ByteEnableMask ByteEnableMask bitwise to ignore t= he ByteEnable setting. + + @retval EFI_INVALID_PARAMETER If Type is invalid, + If Length is invalid, + If Address is invalid, + EFI_ACCESS_DENIED If the SmmReadyToLock event has be= en triggered, + EFI_OUT_OF_RESOURCES If run out of IoTrap register reso= urce, + If run out of SMM memory pool, + EFI_SUCCESS IoTrap register successfully. +**/ +EFI_STATUS +IoTrapRegisterHelper ( + OUT EFI_HANDLE *DispatchHandle, + IN EFI_SMM_HANDLER_ENTRY_POINT2 IoTrapDispatchFunction, + IN IO_TRAP_EX_DISPATCH_CALLBACK IoTrapExDispatchFunctio= n, + IN OUT UINT16 *Address, + IN UINT16 Length, + IN IO_TRAP_EX_DISPATCH_TYPE Type, + IN UINT8 ByteEnable, + IN UINT8 ByteEnableMask + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS BaseAddress; + UINT32 UsedLength; + UINT8 TrapHandlerNum; + UINT32 IoTrapRegLowDword; + UINT32 IoTrapRegHighDword; + BOOLEAN TempMergeDisable; + + DEBUG ((DEBUG_INFO, "IoTrapRegisterHelper\n")); + DEBUG ((DEBUG_INFO, "Address:%x \n", *Address)); + DEBUG ((DEBUG_INFO, "Length:%x \n", Length)); + DEBUG ((DEBUG_INFO, "Type:%x \n", Type)); + DEBUG ((DEBUG_INFO, "ByteEnable:%x \n", ByteEnable)); + DEBUG ((DEBUG_INFO, "ByteEnableMask:%x \n", ByteEnableMask)); + + // + // Return error if the type is invalid + // + if (Type >=3D IoTrapExTypeMaximum) { + DEBUG ((DEBUG_ERROR, "The Dispatch Type %0X is invalid! \n", Type)); + return EFI_INVALID_PARAMETER; + } + // + // Return error if the Length is invalid + // + if (Length < 1 || Length > GENERIC_IOTRAP_SIZE) { + DEBUG ((DEBUG_ERROR, "The Dispatch Length %0X is invalid! \n", Length)= ); + return EFI_INVALID_PARAMETER; + } + // + // Return error if the address is invalid + // PCH supports non-aligned address but (Address % 4 + Length) must not = be more than 4 + // + if (((Length & (Length - 1)) !=3D 0) && (Length !=3D 3)) { + DEBUG ((DEBUG_ERROR, "The Dispatch Length is not power of 2 \n")); + return EFI_INVALID_PARAMETER; + } + + if (((Length >=3D 4) && (*Address & 0x3)) || + ((Length < 4) && (((*Address & 0x3) + Length) > 4))) { + DEBUG ((DEBUG_ERROR, "PCH does not support Dispatch Address %0X and Le= ngth %0X combination \n", *Address, Length)); + return EFI_INVALID_PARAMETER; + } + + if ((Length >=3D 4) && ((*Address & (Length - 1)) !=3D 0)) { + DEBUG ((DEBUG_ERROR, "Dispatch Address %0X is not aligned to the Lengt= h %0X \n", *Address, Length)); + return EFI_INVALID_PARAMETER; + } + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock ev= ent has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + if (*Address) { + TempMergeDisable =3D TRUE; + }else { + TempMergeDisable =3D FALSE; + } + // + // Loop through the first IO Trap handler, looking for the suitable hand= ler + // + for (TrapHandlerNum =3D 0; TrapHandlerNum < IO_TRAP_HANDLER_NUM; TrapHan= dlerNum++) { + // + // Get information from Io Trap handler register + // + IoTrapRegLowDword =3D PchPcrRead32 (PID_PSTH, R_PSTH_PCR_TRPREG0 + Tra= pHandlerNum * 8); + + // + // Check if the IO Trap handler is not used + // + if (AddressFromLowDword (IoTrapRegLowDword) =3D=3D 0) { + // + // Search available IO address and allocate it if the IO address is= 0 + // + BaseAddress =3D *Address; + if (BaseAddress =3D=3D 0) { + // + // Allocate 256 byte range from GCD for common pool usage + // + if ((PcdGet8 (PcdEfiGcdAllocateType) =3D=3D EfiGcdAllocateMaxAddre= ssSearchBottomUp) || (PcdGet8 (PcdEfiGcdAllocateType) =3D=3D EfiGcdAllocate= MaxAddressSearchTopDown)) { + BaseAddress =3D 0xFFFF; + } + Status =3D gDS->AllocateIoSpace ( + PcdGet8 (PcdEfiGcdAllocateType), + EfiGcdIoTypeIo, + 8, + GENERIC_IOTRAP_SIZE, + &BaseAddress, + mDriverImageHandle, + NULL + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Can't find any available IO address! \n"))= ; + return EFI_OUT_OF_RESOURCES; + } + + *Address =3D (UINT16) BaseAddress; + UsedLength =3D GENERIC_IOTRAP_SIZE; + mIoTrapData.Entry[TrapHandlerNum].TrapUsedLength =3D Length; + mIoTrapData.Entry[TrapHandlerNum].ReservedAcpiIoResource =3D TRUE; + UpdateIoTrapAcpiResources (TrapHandlerNum, BaseAddress, TRUE); + } else { + BaseAddress &=3D B_PSTH_PCR_TRPREG_AD; + UsedLength =3D Length; + } + + Status =3D PchInternalIoTrapSmiRegister ( + mIoTrapData.Entry[TrapHandlerNum].CallbackDispatcher, + TrapHandlerNum, + &mIoTrapHandle + ); + + ASSERT_EFI_ERROR (Status); + mIoTrapData.Entry[TrapHandlerNum].IoTrapHandle =3D mIoTrapHandle; + + // + // Fill in the Length, address and Enable the IO Trap SMI + // + IoTrapRegLowDword =3D (UINT32) (((UsedLength - 1) & ~(BIT1 + BIT0)) = << 16) | + (UINT16) BaseAddress | + B_PSTH_PCR_TRPREG_TSE; + + if (UsedLength < 4) { + // + // The 4 bits is the Byte Enable Mask bits to indicate which byte = that are trapped. + // The input ByteEnable and ByteEnableMask are ignored in this cas= e. + // + IoTrapRegHighDword =3D (((1 << UsedLength) - 1) << ((*Address & 0= x3) + (N_PSTH_PCR_TRPREG_BEM - 32))) | + (UINT32) (Type << N_PSTH_PCR_TRPREG_RWIO); + } else { + // + // Fill in the ByteEnable, ByteEnableMask, and Type of Io Trap reg= ister + // + IoTrapRegHighDword =3D ((ByteEnableMask & 0xF) << (N_PSTH_PCR_TRP= REG_BEM - 32)) | + ((ByteEnable & 0xF) << (N_PSTH_PCR_TRPREG_BE - 32)) | + (UINT32) (Type << N_PSTH_PCR_TRPREG_RWIO); + } + SetIoTrapHighDword (TrapHandlerNum, IoTrapRegHighDword, TRUE); + SetIoTrapLowDword (TrapHandlerNum, IoTrapRegLowDword, TRUE); + // + // Set MergeDisable flag of the registered IoTrap + // + mIoTrapData.Entry[TrapHandlerNum].MergeDisable =3D TempMergeDisable; + } else { + // + // Check next handler if MergeDisable is TRUE or the registered IoTr= ap if MergeDisable is TRUE + // If the Io Trap register is used by IoTrapEx protocol, then the Me= rgeDisable will be FALSE. + // + if ((TempMergeDisable =3D=3D TRUE) || (mIoTrapData.Entry[TrapHandler= Num].MergeDisable =3D=3D TRUE)) { + continue; + } + // + // The IO Trap handler is used, calculate the Length + // + UsedLength =3D LengthFromLowDword (IoTrapRegLowDword); + BaseAddress =3D AddressFromLowDword (IoTrapRegLowDword); + // + // Assign an addfress from common pool if the caller's address is 0 + // + if (*Address =3D=3D 0) { + // + // Check next handler if it's fully used + // + if (mIoTrapData.Entry[TrapHandlerNum].TrapUsedLength >=3D GENERIC_= IOTRAP_SIZE) { + continue; + } + // + // Check next handler if it's not for a common pool + // + if (UsedLength < GENERIC_IOTRAP_SIZE) { + continue; + } + // + // Check next handler if the size is too big + // + if (Length >=3D (UINT16) GENERIC_IOTRAP_SIZE - mIoTrapData.Entry[T= rapHandlerNum].TrapUsedLength) { + continue; + } + // + // For common pool, we don't need to change the BaseAddress and Us= edLength + // + *Address =3D (UINT16) (BaseAddress + mIoTrapData.Entry[TrapHandler= Num].TrapUsedLength); + mIoTrapData.Entry[TrapHandlerNum].TrapUsedLength +=3D Length; + } + // + // Only set RWM bit when we need both read and write cycles. + // + IoTrapRegHighDword =3D PchPcrRead32 (PID_PSTH, R_PSTH_PCR_TRPREG0 + = TrapHandlerNum * 8 + 4); + if ((IoTrapRegHighDword & B_PSTH_PCR_TRPREG_RWM) =3D=3D 0 && + (UINT32) ((IoTrapRegHighDword & B_PSTH_PCR_TRPREG_RWIO) >> N_PST= H_PCR_TRPREG_RWIO) !=3D + (UINT32) Type) { + IoTrapRegHighDword =3D ((IoTrapRegHighDword | B_PSTH_PCR_TRPREG_RW= M) & ~B_PSTH_PCR_TRPREG_RWIO); + SetIoTrapHighDword (TrapHandlerNum, IoTrapRegHighDword, TRUE); + } + } + break; + } + + if (TrapHandlerNum >=3D IO_TRAP_HANDLER_NUM) { + DEBUG ((DEBUG_ERROR, "All IO Trap handler is used, no available IO Tra= p handler! \n")); + return EFI_OUT_OF_RESOURCES; + } + // + // Create database record and add to database + // + Status =3D gSmst->SmmAllocatePool ( + EfiRuntimeServicesData, + sizeof (IO_TRAP_RECORD), + (VOID **) &mIoTrapRecord + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to allocate memory for mIoTrapRecord! \n"= )); + return EFI_OUT_OF_RESOURCES; + } + // + // Gather information about the registration request + // + mIoTrapRecord->Signature =3D IO_TRAP_RECORD_SIGNATURE; + mIoTrapRecord->Context.Address =3D *Address; + mIoTrapRecord->Context.Length =3D Length; + mIoTrapRecord->Context.Type =3D Type; + mIoTrapRecord->Context.ByteEnable =3D ByteEnable; + mIoTrapRecord->Context.ByteEnableMask =3D ByteEnableMask; + mIoTrapRecord->IoTrapCallback =3D IoTrapDispatchFunction; + mIoTrapRecord->IoTrapExCallback =3D IoTrapExDispatchFunction; + mIoTrapRecord->IoTrapNumber =3D TrapHandlerNum; + + InsertTailList (&(mIoTrapData.Entry[TrapHandlerNum].CallbackDataBase), &= mIoTrapRecord->Link); + + // + // Child's handle will be the address linked list link in the record + // + *DispatchHandle =3D (EFI_HANDLE) (&mIoTrapRecord->Link); + + DEBUG ((DEBUG_INFO, "Result Address:%x \n", *Address)); + DEBUG ((DEBUG_INFO, "Result Length:%x \n", Length)); + + return EFI_SUCCESS; +} + +/** + IoTrap unregistratrion helper fucntion. + + @param[in] DispatchHandle Handle of dispatch function + + @retval EFI_INVALID_PARAMETER If DispatchHandle is invalid, + EFI_ACCESS_DENIED If the SmmReadyToLock event has be= en triggered, + EFI_SUCCESS IoTrap unregister successfully. +**/ +EFI_STATUS +IoTrapUnRegisterHelper ( + IN EFI_HANDLE DispatchHandle + ) +{ + EFI_STATUS Status; + IO_TRAP_RECORD *RecordToDelete; + UINT32 IoTrapRegLowDword; + EFI_PHYSICAL_ADDRESS BaseAddress; + UINT32 UsedLength; + UINT8 TrapHandlerNum; + UINT8 LengthIndex; + BOOLEAN RequireToDisableIoTrapHandler; + + if (DispatchHandle =3D=3D 0) { + return EFI_INVALID_PARAMETER; + } + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "UnRegister is not allowed if the SmmReadyToLock = event has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + RecordToDelete =3D IO_TRAP_RECORD_FROM_LINK (DispatchHandle); + // + // Take the entry out of the linked list + // + if (RecordToDelete->Link.ForwardLink =3D=3D (LIST_ENTRY *) EFI_BAD_POINT= ER) { + return EFI_INVALID_PARAMETER; + } + + RequireToDisableIoTrapHandler =3D FALSE; + // + // Loop through the first IO Trap handler, looking for the suitable hand= ler + // + TrapHandlerNum =3D RecordToDelete->IoTrapNumber; + + if (mIoTrapData.Entry[TrapHandlerNum].MergeDisable) { + // + // Disable the IO Trap handler if it's the only child of the Trap hand= ler + // + RequireToDisableIoTrapHandler =3D TRUE; + } else { + // + // Get information from Io Trap handler register + // + IoTrapRegLowDword =3D PchPcrRead32 (PID_PSTH, R_PSTH_PCR_TRPREG0 + Tra= pHandlerNum * 8); + + // + // Check next Io Trap handler if the IO Trap handler is not used + // + if (AddressFromLowDword (IoTrapRegLowDword) !=3D 0) { + + UsedLength =3D LengthFromLowDword (IoTrapRegLowDword); + BaseAddress =3D AddressFromLowDword (IoTrapRegLowDword); + + // + // Check if it's the maximum address of the Io Trap handler + // + if ((UINTN)(BaseAddress + UsedLength) =3D=3D (UINTN)(RecordToDelete-= >Context.Address + RecordToDelete->Context.Length)) { + + if (BaseAddress =3D=3D RecordToDelete->Context.Address) { + // + // Disable the IO Trap handler if it's the only child of the Tra= p handler + // + RequireToDisableIoTrapHandler =3D TRUE; + } else { + // + // Calculate the new IO Trap handler Length + // + UsedLength =3D UsedLength - RecordToDelete->Context.Length; + // + // Check the alignment is dword * power of 2 or not + // + for (LengthIndex =3D 0; LengthIndex < sizeof (mLengthTable) / si= zeof (UINT16); LengthIndex++) { + if (UsedLength =3D=3D mLengthTable[LengthIndex]) { + break; + } + } + // + // Do not decrease the length if the alignment is not dword * po= wer of 2 + // + if (LengthIndex < sizeof (mLengthTable) / sizeof (UINT16)) { + // + // Decrease the length to prevent the IO trap SMI + // + IoTrapRegLowDword =3D (UINT32) ((((UsedLength - 1) &~(BIT1 + B= IT0)) << 16) | BaseAddress | B_PSTH_PCR_TRPREG_TSE); + } + SetIoTrapLowDword (TrapHandlerNum, IoTrapRegLowDword, TRUE); + } + } + } + } + + if (RequireToDisableIoTrapHandler) { + mIoTrapHandle =3D mIoTrapData.Entry[TrapHandlerNum].IoTrapHandle; + Status =3D PchInternalIoTrapSmiUnRegister (mIoTrapHandle); + ASSERT_EFI_ERROR (Status); + + SetIoTrapLowDword (TrapHandlerNum, 0, TRUE); + SetIoTrapHighDword (TrapHandlerNum, 0, TRUE); + // + // Also clear pending IOTRAP status. + // + ClearPendingIoTrapStatus (TrapHandlerNum); + + mIoTrapData.Entry[TrapHandlerNum].IoTrapHandle =3D 0; + mIoTrapData.Entry[TrapHandlerNum].MergeDisable =3D FALSE; + if (mIoTrapData.Entry[TrapHandlerNum].ReservedAcpiIoResource =3D=3D TR= UE) { + mIoTrapData.Entry[TrapHandlerNum].ReservedAcpiIoResource =3D FALSE; + UpdateIoTrapAcpiResources (TrapHandlerNum, 0, FALSE); + } + } + + RemoveEntryList (&RecordToDelete->Link); + Status =3D gSmst->SmmFreePool (RecordToDelete); + ASSERT_EFI_ERROR (Status); + + return EFI_SUCCESS; +} + +/** + Register a new IO Trap SMI dispatch function with a parent SMM driver. + The caller will provide information about the IO trap characteristics vi= a + the context. This includes base address, length, read vs. r/w, etc. + This function will autoallocate IO base address from a common pool if th= e base address is 0, + and the RegisterContext Address field will be updated. + The service will not perform GCD allocation if the base address is non-z= ero. + In this case, the caller is responsible for the existence and allocation= of the + specific IO range. + This function looks for the suitable handler and Register a new IoTrap h= andler + if the IO Trap handler is not used. It also enable the IO Trap Range to = generate + SMI. + + @param[in] This Pointer to the EFI_SMM_IO_TRAP_DISPATCH2= _PROTOCOL instance. + @param[in] DispatchFunction Pointer to dispatch function to be invok= ed for + this SMI source. + @param[in, out] RegisterContext Pointer to the dispatch function's conte= xt. + The caller fills this context in before = calling + the register function to indicate to the= register + function the IO trap SMI source for whic= h the dispatch + function should be invoked. This may no= t be NULL. + If the registration address is not 0, it= 's caller's responsibility + to reserve the IO resource in ACPI. + @param[out] DispatchHandle Handle of dispatch function, for when in= terfacing + with the parent SMM driver, will be the = address of linked + list link in the call back record. This= may not be NULL. + + @retval EFI_SUCCESS The dispatch function has been successfu= lly + registered and the SMI source has been e= nabled. + @retval EFI_DEVICE_ERROR The driver was unable to enable the SMI = source. + @retval EFI_OUT_OF_RESOURCES Insufficient resources are available + @retval EFI_INVALID_PARAMETER Address requested is already in use. + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLo= ck event has been triggered +**/ +EFI_STATUS +EFIAPI +IoTrapRegister ( + IN CONST EFI_SMM_IO_TRAP_DISPATCH2_PROTOCOL *This, + IN EFI_SMM_HANDLER_ENTRY_POINT2 DispatchFunction, + IN OUT EFI_SMM_IO_TRAP_REGISTER_CONTEXT *RegisterContext, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + + DEBUG ((DEBUG_INFO, "IoTrapRegister\n")); + Status =3D IoTrapRegisterHelper ( + DispatchHandle, + DispatchFunction, + NULL, + &(RegisterContext->Address), + RegisterContext->Length, + (IO_TRAP_EX_DISPATCH_TYPE) RegisterContext->Type, + 0x00, + 0x0F); + + if (!EFI_ERROR (Status)) { + SmiHandlerProfileRegisterHandler (&gEfiSmmIoTrapDispatch2ProtocolGuid,= DispatchFunction, (UINTN)RETURN_ADDRESS (0), NULL, 0); + } + return Status; +} + +/** + Unregister a child SMI source dispatch function with a parent SMM driver= . + + @param[in] This Pointer to the EFI_SMM_IO_TRAP_DISPATCH2= _PROTOCOL instance. + @param[in] DispatchHandle Handle of dispatch function to deregiste= r. + + @retval EFI_SUCCESS The dispatch function has been successfu= lly + unregistered and the SMI source has been= disabled + if there are no other registered child d= ispatch + functions for this SMI source. + @retval EFI_INVALID_PARAMETER Handle is invalid. + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLo= ck event has been triggered +**/ +EFI_STATUS +EFIAPI +IoTrapUnRegister ( + IN CONST EFI_SMM_IO_TRAP_DISPATCH2_PROTOCOL *This, + IN EFI_HANDLE DispatchHandle + ) +{ + IO_TRAP_RECORD *RecordToDelete; + + RecordToDelete =3D IO_TRAP_RECORD_FROM_LINK (DispatchHandle); + SmiHandlerProfileUnregisterHandler (&gEfiSmmIoTrapDispatch2ProtocolGuid,= RecordToDelete->IoTrapCallback, NULL, 0); + return IoTrapUnRegisterHelper (DispatchHandle); +} + +/** + Register a new IO Trap Ex SMI dispatch function. + + @param[in] This Pointer to the IO_TRAP_EX_DISPATCH_PROTO= COL instance. + @param[in] DispatchFunction Pointer to dispatch function to be invok= ed for + this SMI source. + @param[in] RegisterContext Pointer to the dispatch function's conte= xt. + The caller fills this context in before = calling + the register function to indicate to the= register + function the IO trap Ex SMI source for w= hich the dispatch + function should be invoked. This MUST n= ot be NULL. + @param[out] DispatchHandle Handle of dispatch function. + + @retval EFI_SUCCESS The dispatch function has been successfu= lly + registered and the SMI source has been e= nabled. + @retval EFI_OUT_OF_RESOURCES Insufficient resources are available + @retval EFI_INVALID_PARAMETER Address requested is already in use. + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLo= ck event has been triggered +**/ +EFI_STATUS +EFIAPI +IoTrapExRegister ( + IN IO_TRAP_EX_DISPATCH_PROTOCOL *This, + IN IO_TRAP_EX_DISPATCH_CALLBACK DispatchFunction, + IN IO_TRAP_EX_REGISTER_CONTEXT *RegisterContext, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + + DEBUG ((DEBUG_INFO, "PchSmmIoTrapExRegister\n")); + // + // Return error if length is less than 4 and not power of 2. + // + if ((RegisterContext->Length < 4) || ((RegisterContext->Length & (Regist= erContext->Length - 1)) !=3D 0)) { + DEBUG ((DEBUG_ERROR, "The Dispatch Length is not power of 2 \n")); + return EFI_INVALID_PARAMETER; + } + + Status =3D IoTrapRegisterHelper ( + DispatchHandle, + NULL, + DispatchFunction, + &(RegisterContext->Address), + RegisterContext->Length, + RegisterContext->Type, + RegisterContext->ByteEnable, + RegisterContext->ByteEnableMask); + + if (!EFI_ERROR (Status)) { + SmiHandlerProfileRegisterHandler (&gIoTrapExDispatchProtocolGuid, (EFI= _SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NULL= , 0); + } + return Status; +} + +/** + Unregister a SMI source dispatch function. + This function is unsupported. + + @param[in] This Pointer to the IO_TRAP_EX_DISPATCH_PROTO= COL instance. + @param[in] DispatchHandle Handle of dispatch function to deregiste= r. + + @retval EFI_UNSUPPORTED The function is unsupported. +**/ +EFI_STATUS +EFIAPI +IoTrapExUnRegister ( + IN IO_TRAP_EX_DISPATCH_PROTOCOL *This, + IN EFI_HANDLE DispatchHandle + ) +{ + IO_TRAP_RECORD *RecordToDelete; + + RecordToDelete =3D IO_TRAP_RECORD_FROM_LINK (DispatchHandle); + SmiHandlerProfileUnregisterHandler (&gIoTrapExDispatchProtocolGuid, Reco= rdToDelete->IoTrapCallback, NULL, 0); + return IoTrapUnRegisterHelper (DispatchHandle); +} + +/** + Pause IoTrap callback function. + + This function disables the SMI enable of IoTrap according to the Dispatc= hHandle, + which is returned by IoTrap callback registration. It only supports the = DispatchHandle + with MergeDisable TRUE and address not zero. + + NOTE: This call does not guarantee all pending IO cycles to be synchroni= zed + and pending IO cycles issued before this call might not be trapped= . + + @param[in] This Pointer to the PCH_SMM_IO_TRAP_CONTROL_P= ROTOCOL instance. + @param[in] DispatchHandle Handle of the child service to change st= ate. + + @retval EFI_SUCCESS This operation is complete. + @retval EFI_INVALID_PARAMETER The DispatchHandle is invalid. + @retval EFI_ACCESS_DENIED The SMI status is alrady PAUSED. +**/ +EFI_STATUS +EFIAPI +IoTrapControlPause ( + IN PCH_SMM_IO_TRAP_CONTROL_PROTOCOL *This, + IN EFI_HANDLE DispatchHandle + ) +{ + IO_TRAP_RECORD *IoTrapRecord; + UINT32 IoTrapRegLowDword; + UINT32 IoTrapRegHighDword; + EFI_PHYSICAL_ADDRESS BaseAddress; + UINT32 UsedLength; + UINT8 TrapHandlerNum; + BOOLEAN TempMergeDisable; + BOOLEAN DisableIoTrap; + + if (DispatchHandle =3D=3D 0) { + return EFI_INVALID_PARAMETER; + } + + IoTrapRecord =3D IO_TRAP_RECORD_FROM_LINK (DispatchHandle); + + if (IoTrapRecord->Context.Address) { + TempMergeDisable =3DTRUE; + }else { + TempMergeDisable =3D FALSE; + } + + if ((IoTrapRecord->Signature !=3D IO_TRAP_RECORD_SIGNATURE) || + (TempMergeDisable !=3D TRUE) || + (IoTrapRecord->Context.Address =3D=3D 0) || + (IoTrapRecord->Context.Length =3D=3D 0)) { + return EFI_INVALID_PARAMETER; + } + + for (TrapHandlerNum =3D 0; TrapHandlerNum < IO_TRAP_HANDLER_NUM; TrapHan= dlerNum++) { + // + // This IoTrap register should be merge disabled. + // + if (mIoTrapData.Entry[TrapHandlerNum].MergeDisable !=3D TRUE) { + continue; + } + IoTrapRegLowDword =3D PchPcrRead32 (PID_PSTH, R_PSTH_PCR_TRPREG0 + Tra= pHandlerNum * 8); + IoTrapRegHighDword =3D PchPcrRead32 (PID_PSTH, R_PSTH_PCR_TRPREG0 + Tr= apHandlerNum * 8 + 4); + // + // Depending on the usage, we will obtain the UsedLength and BaseAddre= ss differently + // If the registered trap length is less than 4, we obtain the length = from Byte Enable Mask + // In the other hand, we obtain the length from Address Mask + // + if (ByteEnableMaskFromHighDword (IoTrapRegHighDword) !=3D 0xF) { + UsedLength =3D (UINT32) (HighBitSet32 (IoTrapRegHighDword & 0xF0) - = LowBitSet32 (IoTrapRegHighDword & 0xF0) + 1); + BaseAddress =3D AddressFromLowDword (IoTrapRegLowDword) + LowBitSet3= 2 (ByteEnableMaskFromHighDword (IoTrapRegHighDword)); + } else { + UsedLength =3D LengthFromLowDword (IoTrapRegLowDword); + BaseAddress =3D AddressFromLowDword (IoTrapRegLowDword); + } + + // + // The address and length of record matches the IoTrap register's. + // + DisableIoTrap =3D FALSE; + if ((IoTrapRecord->IoTrapExCallback !=3D NULL) && + IsIoTrapExContentMatched (IoTrapRecord, IoTrapRegLowDword, IoTrapR= egHighDword)) { + DisableIoTrap =3D TRUE; + } else if ((BaseAddress =3D=3D IoTrapRecord->Context.Address) && + (UsedLength =3D=3D IoTrapRecord->Context.Length )) { + DisableIoTrap =3D TRUE; + } + + if (DisableIoTrap) { + // + // Check if status matched. + // If this is already Paused, return warning status. + // + if ((IoTrapRegLowDword & B_PSTH_PCR_TRPREG_TSE) =3D=3D 0) { + return EFI_ACCESS_DENIED; + } + // + // Clear IoTrap register SMI enable bit + // + IoTrapRegLowDword &=3D (~B_PSTH_PCR_TRPREG_TSE); + SetIoTrapLowDword (TrapHandlerNum, IoTrapRegLowDword, FALSE); + // + // Also clear pending IOTRAP status. + // + ClearPendingIoTrapStatus (TrapHandlerNum); + return EFI_SUCCESS; + } + } + return EFI_INVALID_PARAMETER; +} + +/** + Resume IoTrap callback function. + + This function enables the SMI enable of IoTrap according to the Dispatch= Handle, + which is returned by IoTrap callback registration. It only supports the = DispatchHandle + with MergeDisable TRUE and address not zero. + + @param[in] This Pointer to the PCH_SMM_IO_TRAP_CONTROL_P= ROTOCOL instance. + @param[in] DispatchHandle Handle of the child service to change st= ate. + + @retval EFI_SUCCESS This operation is complete. + @retval EFI_INVALID_PARAMETER The DispatchHandle is invalid. + @retval EFI_ACCESS_DENIED The SMI status is alrady RESUMED. +**/ +EFI_STATUS +EFIAPI +IoTrapControlResume ( + IN PCH_SMM_IO_TRAP_CONTROL_PROTOCOL *This, + IN EFI_HANDLE DispatchHandle + ) +{ + IO_TRAP_RECORD *IoTrapRecord; + UINT32 IoTrapRegLowDword; + UINT32 IoTrapRegHighDword; + EFI_PHYSICAL_ADDRESS BaseAddress; + UINT32 UsedLength; + UINT8 TrapHandlerNum; + BOOLEAN TempMergeDisable; + BOOLEAN EnableIoTrap; + + if (DispatchHandle =3D=3D 0) { + return EFI_INVALID_PARAMETER; + } + IoTrapRecord =3D IO_TRAP_RECORD_FROM_LINK (DispatchHandle); + + if (IoTrapRecord->Context.Address) { + TempMergeDisable =3D TRUE; + }else { + TempMergeDisable =3D FALSE; + } + + if ((IoTrapRecord->Signature !=3D IO_TRAP_RECORD_SIGNATURE) || + (TempMergeDisable !=3D TRUE) || + (IoTrapRecord->Context.Address =3D=3D 0) || + (IoTrapRecord->Context.Length =3D=3D 0)) { + return EFI_INVALID_PARAMETER; + } + + for (TrapHandlerNum =3D 0; TrapHandlerNum < IO_TRAP_HANDLER_NUM; TrapHan= dlerNum++) { + // + // This IoTrap register should be merge disabled. + // + if (mIoTrapData.Entry[TrapHandlerNum].MergeDisable !=3D TRUE) { + continue; + } + IoTrapRegLowDword =3D PchPcrRead32 (PID_PSTH, R_PSTH_PCR_TRPREG0 + Tra= pHandlerNum * 8); + IoTrapRegHighDword =3D PchPcrRead32 (PID_PSTH, R_PSTH_PCR_TRPREG0 + Tr= apHandlerNum * 8 + 4); + // + // Depending on the usage, we will obtain the UsedLength and BaseAddre= ss differently + // If the registered trap length is less than 4, we obtain the length = from Byte Enable Mask + // In the other hand, we obtain the length from Address Mask + // + if (ByteEnableMaskFromHighDword (IoTrapRegHighDword) !=3D 0xF) { + UsedLength =3D (UINT32) (HighBitSet32 (IoTrapRegHighDword & 0xF0) -= LowBitSet32 (IoTrapRegHighDword & 0xF0) + 1); + BaseAddress =3D AddressFromLowDword (IoTrapRegLowDword) + LowBitSet3= 2 (ByteEnableMaskFromHighDword (IoTrapRegHighDword)); + } else { + UsedLength =3D LengthFromLowDword (IoTrapRegLowDword); + BaseAddress =3D AddressFromLowDword (IoTrapRegLowDword); + } + + // + // The address and length of record matches the IoTrap register's. + // + EnableIoTrap =3D FALSE; + if ((IoTrapRecord->IoTrapExCallback !=3D NULL) && + IsIoTrapExContentMatched (IoTrapRecord, IoTrapRegLowDword, IoTrapR= egHighDword)) { + EnableIoTrap =3D TRUE; + } else if ((BaseAddress =3D=3D IoTrapRecord->Context.Address) && + (UsedLength =3D=3D IoTrapRecord->Context.Length )) { + EnableIoTrap =3D TRUE; + } + + if (EnableIoTrap) { + // + // Check if status matched. + // If this is already Resume, return warning status. + // + if ((IoTrapRegLowDword & B_PSTH_PCR_TRPREG_TSE) !=3D 0) { + return EFI_ACCESS_DENIED; + } + // + // Set IoTrap register SMI enable bit + // + IoTrapRegLowDword |=3D (B_PSTH_PCR_TRPREG_TSE); + SetIoTrapLowDword (TrapHandlerNum, IoTrapRegLowDword, FALSE); + return EFI_SUCCESS; + } + } + return EFI_INVALID_PARAMETER; +} + +/** + The IoTrap module abstracts PCH I/O trapping capabilities for other driv= ers. + This driver manages the limited I/O trap resources. + + @param[in] ImageHandle Image handle for this driver image + + @retval EFI_SUCCESS Driver initialization completed su= ccessfully +**/ +EFI_STATUS +EFIAPI +InstallIoTrap ( + IN EFI_HANDLE ImageHandle + ) +{ + EFI_STATUS Status; + PCH_NVS_AREA_PROTOCOL *PchNvsAreaProtocol; + UINTN TrapHandlerNum; + + // + // Initialize the EFI SMM driver library + // + mDriverImageHandle =3D ImageHandle; + + // + // Initialize the IO TRAP protocol we produce + // + mIoTrapData.Signature =3D IO_TRAP_INSTANCE_SIGNATURE; + mIoTrapData.EfiSmmIoTrapDispatchProtocol.Register =3D IoTrapRegister; + mIoTrapData.EfiSmmIoTrapDispatchProtocol.UnRegister =3D IoTrapUnRegister= ; + + // + // Initialize the IO TRAP EX protocol + // + mIoTrapData.IoTrapExDispatchProtocol.Register =3D IoTrapExRegister= ; + mIoTrapData.IoTrapExDispatchProtocol.UnRegister =3D IoTrapExUnRegist= er; + + // + // Initialize the IO TRAP control protocol. + // + mIoTrapData.PchSmmIoTrapControlProtocol.Pause =3D IoTrapControlPau= se; + mIoTrapData.PchSmmIoTrapControlProtocol.Resume =3D IoTrapControlRes= ume; + + for (TrapHandlerNum =3D 0; TrapHandlerNum < IO_TRAP_HANDLER_NUM; TrapHan= dlerNum++) { + // + // Initialize IO TRAP Callback DataBase + // + InitializeListHead (&(mIoTrapData.Entry[TrapHandlerNum].CallbackDataBa= se)); + } + mIoTrapData.Entry[0].CallbackDispatcher =3D IoTrapDispatcher0; + mIoTrapData.Entry[1].CallbackDispatcher =3D IoTrapDispatcher1; + mIoTrapData.Entry[2].CallbackDispatcher =3D IoTrapDispatcher2; + mIoTrapData.Entry[3].CallbackDispatcher =3D IoTrapDispatcher3; + + // + // Get address of PchNvs structure for later use + // + Status =3D gBS->LocateProtocol (&gPchNvsAreaProtocolGuid, NULL, (VOID **= ) &PchNvsAreaProtocol); + ASSERT_EFI_ERROR (Status); + mPchNvsArea =3D PchNvsAreaProtocol->Area; + + // + // Install protocol interface + // + mIoTrapData.Handle =3D NULL; + Status =3D gSmst->SmmInstallProtocolInterface ( + &mIoTrapData.Handle, + &gEfiSmmIoTrapDispatch2ProtocolGuid, + EFI_NATIVE_INTERFACE, + &mIoTrapData.EfiSmmIoTrapDispatchProtocol + ); + ASSERT_EFI_ERROR (Status); + + Status =3D gSmst->SmmInstallProtocolInterface ( + &mIoTrapData.Handle, + &gIoTrapExDispatchProtocolGuid, + EFI_NATIVE_INTERFACE, + &mIoTrapData.IoTrapExDispatchProtocol + ); + ASSERT_EFI_ERROR (Status); + + Status =3D gSmst->SmmInstallProtocolInterface ( + &mIoTrapData.Handle, + &gPchSmmIoTrapControlGuid, + EFI_NATIVE_INTERFACE, + &mIoTrapData.PchSmmIoTrapControlProtocol + ); + ASSERT_EFI_ERROR (Status); + + return EFI_SUCCESS; +} diff --git a/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/Pc= hSmiDispatch.c b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Sm= m/PchSmiDispatch.c new file mode 100644 index 0000000000..2b70008fee --- /dev/null +++ b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmiDis= patch.c @@ -0,0 +1,2452 @@ +/** @file + This function handle the register/unregister of PCH specific SMI events. + + Copyright (c) 2019 Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include "PchSmmHelpers.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + 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 desc= ription + @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 reg= ister. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source descrip= tion + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database= record + @retval EFI_SUCCESS The database record is created suc= cessfully. +**/ +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 =3D=3D NULL) { + return EFI_INVALID_PARAMETER; + } + + ZeroMem (&Record, sizeof (DATABASE_RECORD)); + // + // Gather information about the registration request + // + Record.Signature =3D DATABASE_RECORD_SIGNATURE; + Record.PchSmiCallback =3D DispatchFunction; + Record.ProtocolType =3D PchSmiDispatchType; + Record.PchSmiType =3D PchSmiType; + + CopyMem (&Record.SrcDesc, SrcDesc, sizeof (PCH_SMM_SOURCE_DESC)); + Status =3D 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 =3D= { + 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 desc= ription 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 desc= ription 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 des= cription table + +**/ +VOID +EFIAPI +NullInitSourceDesc ( + PCH_SMM_SOURCE_DESC *SrcDesc + ) +{ + ZeroMem (SrcDesc, sizeof (PCH_SMM_SOURCE_DESC)); + SrcDesc->En[0].Reg.Type =3D PCH_SMM_ADDR_TYPE_NULL; + SrcDesc->En[1].Reg.Type =3D PCH_SMM_ADDR_TYPE_NULL; + SrcDesc->Sts[0].Reg.Type =3D PCH_SMM_ADDR_TYPE_NULL; + SrcDesc->PmcSmiSts.Reg.Type =3D PCH_SMM_ADDR_TYPE_NULL; +} + +// +// Mch srcdesc +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSrcDescMch =3D { + 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 reg= ister. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source descrip= tion + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database= record + @retval EFI_SUCCESS The database record is created suc= cessfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmRea= dyToLock 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 =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock ev= ent has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status =3D PchSmiRecordInsert ( + &mSrcDescMch, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchTcoSmiMchType, + DispatchHandle + ); + if (!EFI_ERROR (Status)) { + Record =3D DATABASE_RECORD_FROM_LINK (*DispatchHandle); + Record->ClearSource =3D PchTcoSmiClearSource; + PchSmmClearSource (&Record->SrcDesc); + PchSmmEnableSource (&Record->SrcDesc); + SmiHandlerProfileRegisterHandler (&gPchTcoSmiDispatchProtocolGuid, (EF= I_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NUL= L, 0); + } + return Status; +} + +// +// TcoTimeout srcdesc +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSrcDescTcoTimeout= =3D { + 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 reg= ister. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source descrip= tion + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database= record + @retval EFI_SUCCESS The database record is created suc= cessfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmRea= dyToLock 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 =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock ev= ent has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status =3D PchSmiRecordInsert ( + &mSrcDescTcoTimeout, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchTcoSmiTcoTimeoutType, + DispatchHandle + ); + if (!EFI_ERROR (Status)) { + Record =3D DATABASE_RECORD_FROM_LINK (*DispatchHandle); + Record->ClearSource =3D PchTcoSmiClearSource; + PchSmmClearSource (&Record->SrcDesc); + PchSmmEnableSource (&Record->SrcDesc); + SmiHandlerProfileRegisterHandler (&gPchTcoSmiDispatchProtocolGuid, (EF= I_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NUL= L, 0); + } + return Status; +} + +// +// OsTco srcdesc +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSrcDescOsTco =3D = { + 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 reg= ister. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source descrip= tion + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database= record + @retval EFI_SUCCESS The database record is created suc= cessfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmRea= dyToLock 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 =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock ev= ent has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status =3D PchSmiRecordInsert ( + &mSrcDescOsTco, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchTcoSmiOsTcoType, + DispatchHandle + ); + if (!EFI_ERROR (Status)) { + Record =3D DATABASE_RECORD_FROM_LINK (*DispatchHandle); + Record->ClearSource =3D PchTcoSmiClearSource; + PchSmmClearSource (&Record->SrcDesc); + PchSmmEnableSource (&Record->SrcDesc); + SmiHandlerProfileRegisterHandler (&gPchTcoSmiDispatchProtocolGuid, (EF= I_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NUL= L, 0); + } + return Status; +} + +// +// Nmi +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSrcDescNmi =3D { + PCH_SMM_NO_FLAGS, + { + { + { + PCR_ADDR_TYPE, + {PCH_PCR_ADDRESS (PID_ITSS, R_ITSS_PCR_NMI)} + }, + 4, + N_ITSS_PCR_NMI_NMI2SMI_EN + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + PCR_ADDR_TYPE, + {PCH_PCR_ADDRESS (PID_ITSS, R_ITSS_PCR_NMI)} + }, + 4, + N_ITSS_PCR_NMI_NMI2SMI_STS + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_TCO + } +}; + +/** + Enable the Nmi2Smi source +**/ +VOID +PchNmi2SmiEnableSource ( + VOID + ) +{ + // + // The PCR[ITSS].NMI register can only be accessed with BOOT_SAI and SMM= _SAI. + // Since in CFL there is no SMM_SAI it needs PMC assistance to access th= is register. + // + UINT32 ItssNmi; + ItssNmi =3D PmcGetNmiControl (); + PmcSetNmiControl (ItssNmi | B_ITSS_PCR_NMI_NMI2SMI_EN); +} + +/** + Clear the NMI status bit after the SMI handling is done + + @param[in] SrcDesc Pointer to the PCH SMI source desc= ription table +**/ +VOID +EFIAPI +PchNmi2SmiClearSource ( + CONST PCH_SMM_SOURCE_DESC *SrcDesc + ) +{ + // No need to clear NMI2SMI_STS since it's cleared when NMI source is cl= eared. + // Clear TCO status only. + PchSmmClearSource (&mDescSrcTcoSts); +} + +/** + 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 reg= ister. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source descrip= tion + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database= record + @retval EFI_SUCCESS The database record is created suc= cessfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmRea= dyToLock 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 =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock ev= ent has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status =3D PchSmiRecordInsert ( + &mSrcDescNmi, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchTcoSmiNmiType, + DispatchHandle + ); + if (!EFI_ERROR (Status)) { + Record =3D DATABASE_RECORD_FROM_LINK (*DispatchHandle); + // + // Since the NMI2SMI status and enable bit are at the same register, + // it needs separate function to handle the source enable and clear. + // + Record->ClearSource =3D PchNmi2SmiClearSource; + PchNmi2SmiEnableSource (); + SmiHandlerProfileRegisterHandler (&gPchTcoSmiDispatchProtocolGuid, (EF= I_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NUL= L, 0); + } + return Status; +} + +// +// IntruderDetect srcdesc +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSrcDescIntruderDe= t =3D { + 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 ev= ent. + + @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 reg= ister. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source descrip= tion + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database= record + @retval EFI_SUCCESS The database record is created suc= cessfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmRea= dyToLock 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 =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock ev= ent has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status =3D PchSmiRecordInsert ( + &mSrcDescIntruderDet, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchTcoSmiIntruderDetectType, + DispatchHandle + ); + if (!EFI_ERROR (Status)) { + Record =3D DATABASE_RECORD_FROM_LINK (*DispatchHandle); + Record->ClearSource =3D PchTcoSmiClearSourceAndBlock; + PchSmmClearSource (&Record->SrcDesc); + PchSmmEnableSource (&Record->SrcDesc); + SmiHandlerProfileRegisterHandler (&gPchTcoSmiDispatchProtocolGuid, (EF= I_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NUL= L, 0); + } + return Status; +} + +// +// SpiBiosWp srcdesc +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSrcDescSpiBiosWp = =3D { + 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, + { ( + (DEFAULT_PCI_BUS_NUMBER_PCH << 24) | + (PCI_DEVICE_NUMBER_PCH_SPI << 19) | + (PCI_FUNCTION_NUMBER_PCH_SPI << 16) | + R_SPI_CFG_BC + ) } + }, + S_SPI_CFG_BC, + N_SPI_CFG_BC_BLE + }, + }, + { + { + { + PCIE_ADDR_TYPE, + { ( + (DEFAULT_PCI_BUS_NUMBER_PCH << 24) | + (PCI_DEVICE_NUMBER_PCH_SPI << 19) | + (PCI_FUNCTION_NUMBER_PCH_SPI << 16) | + R_SPI_CFG_BC + ) } + }, + 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 =3D PCI_SEGMENT_LIB_ADDRESS ( + DEFAULT_PCI_SEGMENT_NUMBER_PCH, + DEFAULT_PCI_BUS_NUMBER_PCH, + PCI_DEVICE_NUMBER_PCH_SPI, + PCI_FUNCTION_NUMBER_PCH_SPI, + 0 + ); + 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 =3D 1000; + do { + BiosControl =3D 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); +} + +/** + 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 reg= ister. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source descrip= tion + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database= record + @retval EFI_SUCCESS The database record is created suc= cessfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmRea= dyToLock 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 =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock ev= ent has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status =3D PchSmiRecordInsert ( + &mSrcDescSpiBiosWp, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchTcoSmiSpiBiosWpType, + DispatchHandle + ); + if (!EFI_ERROR (Status)) { + Record =3D DATABASE_RECORD_FROM_LINK (*DispatchHandle); + Record->ClearSource =3D PchTcoSpiWpClearSource; + PchTcoSpiWpClearSource (NULL); + // + // It doesn't enable the BIOSLOCK here. Enable it by policy in DXE. + // + SmiHandlerProfileRegisterHandler (&gPchTcoSmiDispatchProtocolGuid, (EF= I_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NUL= L, 0); + } + return Status; +} + +// +// LpcBiosWp srcdesc +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSrcDescLpcBiosWp = =3D { + 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, + { ( + (DEFAULT_PCI_BUS_NUMBER_PCH << 24) | + (PCI_DEVICE_NUMBER_PCH_LPC << 19) | + (PCI_FUNCTION_NUMBER_PCH_LPC << 16) | + R_LPC_CFG_BC + ) } + }, + 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 pro= tect 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 reg= ister. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source descrip= tion + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database= record + @retval EFI_SUCCESS The database record is created suc= cessfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmRea= dyToLock 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 =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock ev= ent has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + if (IsEspiEnabled ()) { + // + // Status is D31F0's PCBC.BWPDS + // + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } + + Status =3D PchSmiRecordInsert ( + &mSrcDescLpcBiosWp, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchTcoSmiLpcBiosWpType, + DispatchHandle + ); + if (!EFI_ERROR (Status)) { + Record =3D DATABASE_RECORD_FROM_LINK (*DispatchHandle); + Record->ClearSource =3D PchTcoSmiClearSource; + PchSmmClearSource (&Record->SrcDesc); + // + // It doesn't enable the BIOSLOCK here. Enable it by policy in DXE. + // + SmiHandlerProfileRegisterHandler (&gPchTcoSmiDispatchProtocolGuid, (EF= I_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NUL= L, 0); + } + return Status; +} + +// +// NEWCENTURY_STS bit that needs to be cleared +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSrcDescNewCentury= =3D { + 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 reg= ister. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source descrip= tion + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database= record + @retval EFI_SUCCESS The database record is created suc= cessfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmRea= dyToLock 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 =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock ev= ent has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status =3D PchSmiRecordInsert ( + &mSrcDescNewCentury, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchTcoSmiNewCenturyType, + DispatchHandle + ); + if (!EFI_ERROR (Status)) { + Record =3D DATABASE_RECORD_FROM_LINK (*DispatchHandle); + Record->ClearSource =3D PchTcoSmiClearSourceAndBlock; + PchSmmClearSource (&Record->SrcDesc); + PchSmmEnableSource (&Record->SrcDesc); + SmiHandlerProfileRegisterHandler (&gPchTcoSmiDispatchProtocolGuid, (EF= I_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NUL= L, 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 der= egister. + + @retval EFI_SUCCESS The dispatch function has been suc= cessfully + unregistered and the SMI source ha= s been disabled + if there are no other registered c= hild dispatch + functions for this SMI source. + @retval EFI_INVALID_PARAMETER Handle is invalid. + @retval EFI_ACCESS_DENIED Return access denied if the SmmRea= dyToLock event has been triggered +**/ +EFI_STATUS +EFIAPI +PchTcoSmiUnRegister ( + IN PCH_TCO_SMI_DISPATCH_PROTOCOL *This, + IN EFI_HANDLE DispatchHandle + ) +{ + DATABASE_RECORD *Record; + EFI_STATUS Status; + + Record =3D DATABASE_RECORD_FROM_LINK (DispatchHandle); + if ((Record->SrcDesc.En[1].Reg.Type =3D=3D ACPI_ADDR_TYPE) && + (Record->SrcDesc.En[1].Reg.Data.pcie.Fields.Dev =3D=3D PCI_DEVICE_NU= MBER_PCH_SPI) && + (Record->SrcDesc.En[1].Reg.Data.pcie.Fields.Fnc =3D=3D PCI_FUNCTION_= NUMBER_PCH_SPI) && + (Record->SrcDesc.En[1].Reg.Data.pcie.Fields.Reg =3D=3D R_SPI_CFG_BC)= && + (Record->SrcDesc.En[1].Bit =3D=3D N_SPI_CFG_BC_BLE)) { + // + // SPI Write Protect cannot be disabled + // + return EFI_ACCESS_DENIED; + } else if ((Record->SrcDesc.En[1].Reg.Type =3D=3D ACPI_ADDR_TYPE) && + (Record->SrcDesc.En[1].Reg.Data.pcie.Fields.Dev =3D=3D PCI_DE= VICE_NUMBER_PCH_LPC) && + (Record->SrcDesc.En[1].Reg.Data.pcie.Fields.Fnc =3D=3D PCI_FU= NCTION_NUMBER_PCH_LPC) && + (Record->SrcDesc.En[1].Reg.Data.pcie.Fields.Reg =3D=3D R_LPC_= CFG_BC) && + (Record->SrcDesc.En[1].Bit =3D=3D N_LPC_CFG_BC_LE)) { + // + // eSPI/LPC Write Protect cannot be disabled + // + return EFI_ACCESS_DENIED; + } + + Status =3D PchSmmCoreUnRegister (NULL, DispatchHandle); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileUnregisterHandler (&gPchTcoSmiDispatchProtocolGuid, R= ecord->Callback, NULL, 0); + } + return Status; +} + + +// +// PcieRpHotPlug srcdesc +// +GLOBAL_REMOVE_IF_UNREFERENCED PCH_SMM_SOURCE_DESC PchPcieSmiRpHotPlugTempl= ate =3D { + PCH_SMM_NO_FLAGS, + { + { + { + PCIE_ADDR_TYPE, + {R_PCH_PCIE_CFG_MPC} + }, + S_PCH_PCIE_CFG_MPC, + N_PCH_PCIE_CFG_MPC_HPME + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + PCIE_ADDR_TYPE, + {R_PCH_PCIE_CFG_SMSCS} + }, + S_PCH_PCIE_CFG_SMSCS, + N_PCH_PCIE_CFG_SMSCS_HPPDM + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_PCI_EXP + } +}; + +/** + The register function used to register SMI handler of PCIE RP hotplug ev= ent. + + @param[in] This The pointer to the protocol itself + @param[in] DispatchFunction Pointer to dispatch function to be= invoked for this SMI source + @param[in] RpIndex Indicate the RP index (0-based) + @param[out] DispatchHandle Handle of dispatch function to reg= ister. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source descrip= tion + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database= record + @retval EFI_SUCCESS The database record is created suc= cessfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmRea= dyToLock event has been triggered +**/ +EFI_STATUS +EFIAPI +PchPcieSmiHotPlugRegister ( + IN PCH_PCIE_SMI_DISPATCH_PROTOCOL *This, + IN PCH_PCIE_SMI_RP_DISPATCH_CALLBACK DispatchFunction, + IN UINTN RpIndex, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + UINTN RpDev; + UINTN RpFun; + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock ev= ent has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + GetPchPcieRpDevFun (RpIndex, &RpDev, &RpFun); + // + // Patch the RP device number and function number of srcdesc. + // + PchPcieSmiRpHotPlugTemplate.En[0].Reg.Data.pcie.Fields.Dev =3D (UINT8) R= pDev; + PchPcieSmiRpHotPlugTemplate.En[0].Reg.Data.pcie.Fields.Fnc =3D (UINT8) R= pFun; + PchPcieSmiRpHotPlugTemplate.Sts[0].Reg.Data.pcie.Fields.Dev =3D (UINT8) = RpDev; + PchPcieSmiRpHotPlugTemplate.Sts[0].Reg.Data.pcie.Fields.Fnc =3D (UINT8) = RpFun; + + Status =3D PchSmiRecordInsert ( + &PchPcieSmiRpHotPlugTemplate, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchPcieSmiRpHotplugType, + DispatchHandle + ); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileRegisterHandler (&gPchPcieSmiDispatchProtocolGuid, (E= FI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NU= LL, 0); + } + PchSmmClearSource (&PchPcieSmiRpHotPlugTemplate); + PchSmmEnableSource (&PchPcieSmiRpHotPlugTemplate); + + return Status; +} + +// +// PcieRpLinkActive srcdesc +// +GLOBAL_REMOVE_IF_UNREFERENCED PCH_SMM_SOURCE_DESC PchPcieSmiRpLinkActiveTe= mplate =3D { + PCH_SMM_NO_FLAGS, + { + { + { + PCIE_ADDR_TYPE, + {R_PCH_PCIE_CFG_MPC} + }, + S_PCH_PCIE_CFG_MPC, + N_PCH_PCIE_CFG_MPC_HPME + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + PCIE_ADDR_TYPE, + {R_PCH_PCIE_CFG_SMSCS} + }, + S_PCH_PCIE_CFG_SMSCS, + N_PCH_PCIE_CFG_SMSCS_HPLAS + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_PCI_EXP + } +}; + +/** + The register function used to register SMI handler of PCIE RP link activ= e 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[in] RpIndex Indicate the RP index (0-based) + @param[out] DispatchHandle Handle of dispatch function to reg= ister. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source descrip= tion + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database= record + @retval EFI_SUCCESS The database record is created suc= cessfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmRea= dyToLock event has been triggered +**/ +EFI_STATUS +EFIAPI +PchPcieSmiLinkActiveRegister ( + IN PCH_PCIE_SMI_DISPATCH_PROTOCOL *This, + IN PCH_PCIE_SMI_RP_DISPATCH_CALLBACK DispatchFunction, + IN UINTN RpIndex, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + UINTN RpDev; + UINTN RpFun; + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock ev= ent has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + GetPchPcieRpDevFun (RpIndex, &RpDev, &RpFun); + // + // Patch the RP device number and function number of srcdesc. + // + PchPcieSmiRpLinkActiveTemplate.En[0].Reg.Data.pcie.Fields.Dev =3D (UINT8= ) RpDev; + PchPcieSmiRpLinkActiveTemplate.En[0].Reg.Data.pcie.Fields.Fnc =3D (UINT8= ) RpFun; + PchPcieSmiRpLinkActiveTemplate.Sts[0].Reg.Data.pcie.Fields.Dev =3D (UINT= 8) RpDev; + PchPcieSmiRpLinkActiveTemplate.Sts[0].Reg.Data.pcie.Fields.Fnc =3D (UINT= 8) RpFun; + + Status =3D PchSmiRecordInsert ( + &PchPcieSmiRpLinkActiveTemplate, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchPcieSmiRpLinkActiveType, + DispatchHandle + ); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileRegisterHandler (&gPchPcieSmiDispatchProtocolGuid, (E= FI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NU= LL, 0); + } + PchSmmClearSource (&PchPcieSmiRpLinkActiveTemplate); + PchSmmEnableSource (&PchPcieSmiRpLinkActiveTemplate); + + return Status; +} + +// +// PcieRpLinkEq srcdesc +// +GLOBAL_REMOVE_IF_UNREFERENCED PCH_SMM_SOURCE_DESC PchPcieSmiRpLinkEqTempla= te =3D { + PCH_SMM_NO_FLAGS, + { + { + { + PCIE_ADDR_TYPE, + {R_PCH_PCIE_CFG_EQCFG1} + }, + S_PCH_PCIE_CFG_EQCFG1, + N_PCH_PCIE_CFG_EQCFG1_LERSMIE + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + PCIE_ADDR_TYPE, + {R_PCH_PCIE_CFG_SMSCS} + }, + S_PCH_PCIE_CFG_SMSCS, + N_PCH_PCIE_CFG_SMSCS_LERSMIS + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_PCI_EXP + } +}; + +/** + The register function used to register SMI handler of PCIE RP Link Equal= ization Request 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[in] RpIndex Indicate the RP index (0-based) + @param[out] DispatchHandle Handle of dispatch function to reg= ister. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source descrip= tion + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database= record + @retval EFI_SUCCESS The database record is created suc= cessfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmRea= dyToLock event has been triggered +**/ +EFI_STATUS +EFIAPI +PchPcieSmiLinkEqRegister ( + IN PCH_PCIE_SMI_DISPATCH_PROTOCOL *This, + IN PCH_PCIE_SMI_RP_DISPATCH_CALLBACK DispatchFunction, + IN UINTN RpIndex, + OUT EFI_HANDLE *DispatchHandle + ) +{ + UINTN RpDev; + UINTN RpFun; + EFI_STATUS Status; + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock ev= ent has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + GetPchPcieRpDevFun (RpIndex, &RpDev, &RpFun); + // + // Patch the RP device number and function number of srcdesc. + // + PchPcieSmiRpLinkEqTemplate.En[0].Reg.Data.pcie.Fields.Dev =3D (UINT8) Rp= Dev; + PchPcieSmiRpLinkEqTemplate.En[0].Reg.Data.pcie.Fields.Fnc =3D (UINT8) Rp= Fun; + PchPcieSmiRpLinkEqTemplate.Sts[0].Reg.Data.pcie.Fields.Dev =3D (UINT8) R= pDev; + PchPcieSmiRpLinkEqTemplate.Sts[0].Reg.Data.pcie.Fields.Fnc =3D (UINT8) R= pFun; + + Status =3D PchSmiRecordInsert ( + &PchPcieSmiRpLinkEqTemplate, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchPcieSmiRpLinkEqType, + DispatchHandle + ); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileRegisterHandler (&gPchPcieSmiDispatchProtocolGuid, (E= FI_SMM_HANDLER_ENTRY_POINT2) DispatchFunction, (UINTN)RETURN_ADDRESS (0), N= ULL, 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 der= egister. + + @retval EFI_SUCCESS The dispatch function has been suc= cessfully + unregistered and the SMI source ha= s been disabled + if there are no other registered c= hild dispatch + functions for this SMI source. + @retval EFI_INVALID_PARAMETER Handle is invalid. + @retval EFI_ACCESS_DENIED Return access denied if the SmmRea= dyToLock event has been triggered +**/ +EFI_STATUS +EFIAPI +PchPcieSmiUnRegister ( + IN PCH_PCIE_SMI_DISPATCH_PROTOCOL *This, + IN EFI_HANDLE DispatchHandle + ) +{ + DATABASE_RECORD *RecordToDelete; + EFI_STATUS Status; + + RecordToDelete =3D DATABASE_RECORD_FROM_LINK (DispatchHandle); + Status =3D PchSmmCoreUnRegister (NULL, DispatchHandle); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileUnregisterHandler (&gPchPcieSmiDispatchProtocolGuid= , RecordToDelete->Callback, NULL, 0); + } + return Status; +} + +// +// Pme srcdesc +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSrcDescPme =3D { + 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 reg= ister. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source descrip= tion + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database= record + @retval EFI_SUCCESS The database record is created suc= cessfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmRea= dyToLock 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 =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock ev= ent has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status =3D PchSmiRecordInsert ( + &mSrcDescPme, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchAcpiSmiPmeType, + DispatchHandle + ); + PchSmmClearSource (&mSrcDescPme); + PchSmmEnableSource (&mSrcDescPme); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileRegisterHandler (&gPchAcpiSmiDispatchProtocolGuid, (E= FI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NU= LL, 0); + } + return Status; +} + +// +// PmeB0 srcdesc +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSrcDescPmeB0 =3D = { + 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 reg= ister. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source descrip= tion + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database= record + @retval EFI_SUCCESS The database record is created suc= cessfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmRea= dyToLock 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 =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock ev= ent has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status =3D PchSmiRecordInsert ( + &mSrcDescPmeB0, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchAcpiSmiPmeB0Type, + DispatchHandle + ); + PchSmmClearSource (&mSrcDescPmeB0); + PchSmmEnableSource (&mSrcDescPmeB0); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileRegisterHandler (&gPchAcpiSmiDispatchProtocolGuid, (E= FI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NU= LL, 0); + } + return Status; +} + +// +// RtcAlarm srcdesc +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSrcDescRtcAlarm = =3D { + 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 reg= ister. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source descrip= tion + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database= record + @retval EFI_SUCCESS The database record is created suc= cessfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmRea= dyToLock 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 =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock ev= ent has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status =3D PchSmiRecordInsert ( + &mSrcDescRtcAlarm, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchAcpiSmiRtcAlarmType, + DispatchHandle + ); + + PchSmmClearSource (&mSrcDescRtcAlarm); + PchSmmEnableSource (&mSrcDescRtcAlarm); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileRegisterHandler (&gPchAcpiSmiDispatchProtocolGuid, (E= FI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NU= LL, 0); + } + return Status; +} + +// +// TmrOverflow srcdesc +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSrcDescTmrOverflo= w =3D { + 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 eve= nt. + + @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 reg= ister. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source descrip= tion + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database= record + @retval EFI_SUCCESS The database record is created suc= cessfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmRea= dyToLock 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 =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock ev= ent has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status =3D PchSmiRecordInsert ( + &mSrcDescTmrOverflow, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchAcpiSmiTmrOverflowType, + DispatchHandle + ); + PchSmmClearSource (&mSrcDescTmrOverflow); + PchSmmEnableSource (&mSrcDescTmrOverflow); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileRegisterHandler (&gPchAcpiSmiDispatchProtocolGuid, (E= FI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NU= LL, 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 der= egister. + + @retval EFI_SUCCESS The dispatch function has been suc= cessfully + unregistered and the SMI source ha= s been disabled + if there are no other registered c= hild dispatch + functions for this SMI source. + @retval EFI_INVALID_PARAMETER Handle is invalid. + @retval EFI_ACCESS_DENIED Return access denied if the SmmRea= dyToLock 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 =3D DATABASE_RECORD_FROM_LINK (DispatchHandle); + Status =3D 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 = =3D { + 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 reg= ister. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source descrip= tion + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database= record + @retval EFI_SUCCESS The database record is created suc= cessfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmRea= dyToLock 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 =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock ev= ent has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status =3D 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 =3D = { + 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 reg= ister. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source descrip= tion + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database= record + @retval EFI_SUCCESS The database record is created suc= cessfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmRea= dyToLock 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 =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock ev= ent has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status =3D PchSmiRecordInsert ( + &mSrcDescMcSmi, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchSmiMcSmiType, + DispatchHandle + ); + PchSmmClearSource (&mSrcDescMcSmi); + PchSmmEnableSource (&mSrcDescMcSmi); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileRegisterHandler (&gPchSmiDispatchProtocolGuid, (EFI_S= MM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NULL, = 0); + } + return Status; +} + +// +// SmBus srcdesc +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSrcDescSmbus =3D = { + 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 reg= ister. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source descrip= tion + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database= record + @retval EFI_SUCCESS The database record is created suc= cessfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmRea= dyToLock 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 =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock ev= ent has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status =3D PchSmiRecordInsert ( + &mSrcDescSmbus, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchSmiSmBusType, + DispatchHandle + ); + PchSmmClearSource (&mSrcDescSmbus); + PchSmmEnableSource (&mSrcDescSmbus); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileRegisterHandler (&gPchSmiDispatchProtocolGuid, (EFI_S= MM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NULL, = 0); + } + return Status; +} + +// +// SpiAsyncSmi srcdesc +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSrcDescSpiAsyncSm= i =3D { + PCH_SMM_NO_FLAGS, + { + { + { + PCIE_ADDR_TYPE, + { ( + (DEFAULT_PCI_BUS_NUMBER_PCH << 24) | + (PCI_DEVICE_NUMBER_PCH_SPI << 19) | + (PCI_FUNCTION_NUMBER_PCH_SPI << 16) | + R_SPI_CFG_BC + ) } + }, + S_SPI_CFG_BC, + N_SPI_CFG_BC_ASE_BWP + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + PCIE_ADDR_TYPE, + { ( + (DEFAULT_PCI_BUS_NUMBER_PCH << 24) | + (PCI_DEVICE_NUMBER_PCH_SPI << 19) | + (PCI_FUNCTION_NUMBER_PCH_SPI << 16) | + R_SPI_CFG_BC + ) } + }, + 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 =3D PCI_SEGMENT_LIB_ADDRESS ( + DEFAULT_PCI_SEGMENT_NUMBER_PCH, + DEFAULT_PCI_BUS_NUMBER_PCH, + PCI_DEVICE_NUMBER_PCH_SPI, + PCI_FUNCTION_NUMBER_PCH_SPI, + 0 + ); + SpiBar0 =3D PciSegmentRead32 (SpiRegBase + R_SPI_CFG_BAR0) & ~(B_SPI_CFG= _BAR0_MASK); + if (SpiBar0 !=3D PCH_SPI_BASE_ADDRESS) { + // + // Temporary disable MSE, and override with SPI reserved MMIO address,= then enable MSE. + // + SpiBar0 =3D PCH_SPI_BASE_ADDRESS; + PciSegmentAnd8 (SpiRegBase + PCI_COMMAND_OFFSET, (UINT8) ~EFI_PCI_COMM= AND_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; + SpiRegBase =3D PCI_SEGMENT_LIB_ADDRESS ( + DEFAULT_PCI_SEGMENT_NUMBER_PCH, + DEFAULT_PCI_BUS_NUMBER_PCH, + PCI_DEVICE_NUMBER_PCH_SPI, + PCI_FUNCTION_NUMBER_PCH_SPI, + 0 + ); + PciSegmentAndThenOr32 ( + SpiRegBase + R_SPI_CFG_BC, + (UINT32) ~B_SPI_CFG_BC_SYNC_SS, + B_SPI_CFG_BC_ASE_BWP + ); + + // + // Clear the source + // + PchSmiSpiAsyncClearSource (NULL); +} + +/** + The register function used to register SMI handler of SPI Asynchronous e= vent. + + @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 reg= ister. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source descrip= tion + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database= record + @retval EFI_SUCCESS The database record is created suc= cessfully. + @retval EFI_ACCESS_DENIED Return access denied if the SmmRea= dyToLock 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 =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock ev= ent has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status =3D PchSmiRecordInsert ( + &mSrcDescSpiAsyncSmi, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchSmiSpiAsyncType, + DispatchHandle + ); + + if (!EFI_ERROR (Status)) { + Record =3D DATABASE_RECORD_FROM_LINK (*DispatchHandle); + Record->ClearSource =3D PchSmiSpiAsyncClearSource; + PchSmiSpiAsyncClearSource (NULL); + PchSmiSpiAsyncEnableSource (); + SmiHandlerProfileRegisterHandler (&gPchSmiDispatchProtocolGuid, (EFI_S= MM_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 der= egister. + + @retval EFI_SUCCESS The dispatch function has been suc= cessfully + unregistered and the SMI source ha= s been disabled + if there are no other registered c= hild dispatch + functions for this SMI source. + @retval EFI_INVALID_PARAMETER Handle is invalid. + @retval EFI_ACCESS_DENIED Return access denied if the SmmRea= dyToLock event has been triggered + @retval EFI_ACCESS_DENIED Return access denied since SPI ayn= c 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 =3D DATABASE_RECORD_FROM_LINK (DispatchHandle); + if ((Record->SrcDesc.En[0].Reg.Type =3D=3D PCIE_ADDR_TYPE) && + (Record->SrcDesc.En[0].Reg.Data.pcie.Fields.Dev =3D=3D PCI_DEVICE_NU= MBER_PCH_SPI) && + (Record->SrcDesc.En[0].Reg.Data.pcie.Fields.Fnc =3D=3D PCI_FUNCTION_= NUMBER_PCH_SPI) && + (Record->SrcDesc.En[0].Reg.Data.pcie.Fields.Reg =3D=3D R_SPI_CFG_BC)= && + (Record->SrcDesc.En[0].Bit =3D=3D N_SPI_CFG_BC_ASE_BWP)) { + SpiRegBase =3D PCI_SEGMENT_LIB_ADDRESS ( + DEFAULT_PCI_SEGMENT_NUMBER_PCH, + DEFAULT_PCI_BUS_NUMBER_PCH, + PCI_DEVICE_NUMBER_PCH_SPI, + PCI_FUNCTION_NUMBER_PCH_SPI, + 0 + ); + if (PciSegmentRead8 (SpiRegBase + R_SPI_CFG_BC) & B_SPI_CFG_BC_BILD) { + // + // SPI Asynchronous SMI cannot be disabled + // + return EFI_ACCESS_DENIED; + } + } + Status =3D PchSmmCoreUnRegister (NULL, DispatchHandle); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileUnregisterHandler (&gPchSmiDispatchProtocolGuid, Reco= rd->Callback, NULL, 0); + } + return Status; +} + + +/** + Declaration of PCH TCO SMI DISPATCH PROTOCOL instance +**/ +PCH_TCO_SMI_DISPATCH_PROTOCOL mPchTcoSmiDispatchProtocol =3D { + 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 PCIE SMI DISPATCH PROTOCOL instance +**/ +PCH_PCIE_SMI_DISPATCH_PROTOCOL mPchPcieSmiDispatchProtocol =3D { + PCH_PCIE_SMI_DISPATCH_REVISION, // Revision + PchPcieSmiUnRegister, // Unregister + PchPcieSmiHotPlugRegister, // PcieRpXHotPlug + PchPcieSmiLinkActiveRegister, // PcieRpXLinkActive + PchPcieSmiLinkEqRegister // PcieRpXLinkEq +}; + +/** + Declaration of PCH ACPI SMI DISPATCH PROTOCOL instance +**/ +PCH_ACPI_SMI_DISPATCH_PROTOCOL mPchAcpiSmiDispatchProtocol =3D { + 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 =3D { + 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 installatio= n +**/ +EFI_STATUS +InstallPchSmiDispatchProtocols ( + VOID + ) +{ + EFI_HANDLE Handle; + EFI_STATUS Status; + + Handle =3D NULL; + Status =3D gSmst->SmmInstallProtocolInterface ( + &Handle, + &gPchTcoSmiDispatchProtocolGuid, + EFI_NATIVE_INTERFACE, + &mPchTcoSmiDispatchProtocol + ); + Status =3D gSmst->SmmInstallProtocolInterface ( + &Handle, + &gPchPcieSmiDispatchProtocolGuid, + EFI_NATIVE_INTERFACE, + &mPchPcieSmiDispatchProtocol + ); + Status =3D gSmst->SmmInstallProtocolInterface ( + &Handle, + &gPchAcpiSmiDispatchProtocolGuid, + EFI_NATIVE_INTERFACE, + &mPchAcpiSmiDispatchProtocol + ); + Status =3D 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; + UINTN RpIndex; + PCH_PCIE_SMI_RP_CONTEXT RpContext; + + PchSmiType =3D Record->PchSmiType; + Status =3D 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_HAN= DLE)&Record->Link); + break; + case PchPcieSmiRpHotplugType: + case PchPcieSmiRpLinkActiveType: + case PchPcieSmiRpLinkEqType: + RpContext.BusNum =3D DEFAULT_PCI_BUS_NUMBER_PCH; + RpContext.DevNum =3D (UINT8) Record->SrcDesc.En[0].Reg.Data.pcie.Fi= elds.Dev; + RpContext.FuncNum =3D (UINT8) Record->SrcDesc.En[0].Reg.Data.pcie.Fi= elds.Fnc; + GetPchPcieRpNumber (RpContext.DevNum, RpContext.FuncNum, &RpIndex); + RpContext.RpIndex =3D (UINT8) RpIndex; + ((PCH_PCIE_SMI_RP_DISPATCH_CALLBACK) (Record->PchSmiCallback)) ((EFI= _HANDLE)&Record->Link, &RpContext); + break; + case PchAcpiSmiPmeType: + case PchAcpiSmiPmeB0Type: + case PchAcpiSmiRtcAlarmType: + case PchAcpiSmiTmrOverflowType: + ((PCH_ACPI_SMI_DISPATCH_CALLBACK) (Record->PchSmiCallback)) ((EFI_HA= NDLE)&Record->Link); + break; + case PchEspiSmiEspiSlaveType: + ((PCH_ESPI_SMI_DISPATCH_CALLBACK) (Record->PchSmiCallback)) ((EFI_HA= NDLE)&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 =3D EFI_UNSUPPORTED; + break; + } + + return Status; +} + +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSrcDescIoTrap[4] = =3D { + // + // PCH I/O Trap register 0 monitor + // + { + PCH_SMM_NO_FLAGS, + { + { + { + PCR_ADDR_TYPE, + {PCH_PCR_ADDRESS (PID_PSTH, R_PSTH_PCR_TRPREG0) } + }, + 4, + 0 + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + PCR_ADDR_TYPE, + {PCH_PCR_ADDRESS (PID_PSTH, R_PSTH_PCR_TRPST) } + }, + 1, + 0 + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_MONITOR + } + }, + // + // PCH I/O Trap register 1 monitor + // + { + PCH_SMM_NO_FLAGS, + { + { + { + PCR_ADDR_TYPE, + {PCH_PCR_ADDRESS (PID_PSTH, R_PSTH_PCR_TRPREG1) } + }, + 4, + 0 + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + PCR_ADDR_TYPE, + {PCH_PCR_ADDRESS (PID_PSTH, R_PSTH_PCR_TRPST) } + }, + 1, + 1 + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_MONITOR + } + }, + // + // PCH I/O Trap register 2 monitor + // + { + PCH_SMM_NO_FLAGS, + { + { + { + PCR_ADDR_TYPE, + {PCH_PCR_ADDRESS (PID_PSTH, R_PSTH_PCR_TRPREG2) } + }, + 4, + 0 + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + PCR_ADDR_TYPE, + {PCH_PCR_ADDRESS (PID_PSTH, R_PSTH_PCR_TRPST) } + }, + 1, + 2 + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_MONITOR + } + }, + // + // PCH I/O Trap register 3 monitor, + // + { + PCH_SMM_NO_FLAGS, + { + { + { + PCR_ADDR_TYPE, + {PCH_PCR_ADDRESS (PID_PSTH, R_PSTH_PCR_TRPREG3) } + }, + 4, + 0 + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + PCR_ADDR_TYPE, + {PCH_PCR_ADDRESS (PID_PSTH, R_PSTH_PCR_TRPST) } + }, + 1, + 3 + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_MONITOR + } + } +}; + +/** + 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 reg= ister. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source descrip= tion + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database= record + @retval EFI_SUCCESS The database record is created suc= cessfully. +**/ +EFI_STATUS +PchInternalIoTrapSmiRegister ( + IN PCH_SMI_DISPATCH_CALLBACK DispatchFunction, + IN UINTN IoTrapIndex, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + + Status =3D PchSmiRecordInsert ( + &mSrcDescIoTrap[IoTrapIndex], + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchIoTrapSmiType, + DispatchHandle + ); + PchSmmClearSource (&mSrcDescIoTrap[IoTrapIndex]); + PchSmmEnableSource (&mSrcDescIoTrap[IoTrapIndex]); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileRegisterHandler (&gEfiSmmIoTrapDispatch2ProtocolGuid,= (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] DispatchHandle Handle of dispatch function to der= egister. + + @retval EFI_SUCCESS The dispatch function has been suc= cessfully + unregistered and the SMI source ha= s been disabled + if there are no other registered c= hild dispatch + functions for this SMI source. + @retval EFI_INVALID_PARAMETER Handle is invalid. +**/ +EFI_STATUS +PchInternalIoTrapSmiUnRegister ( + IN EFI_HANDLE DispatchHandle + ) +{ + DATABASE_RECORD *RecordToDelete; + EFI_STATUS Status; + + RecordToDelete =3D DATABASE_RECORD_FROM_LINK (DispatchHandle); + Status =3D PchSmmCoreUnRegister (NULL, DispatchHandle); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileUnregisterHandler (&gEfiSmmIoTrapDispatch2ProtocolGui= d, RecordToDelete->Callback, NULL, 0); + } + return Status; +} diff --git a/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/Pc= hSmmCore.c b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/Pc= hSmmCore.c new file mode 100644 index 0000000000..9c36103396 --- /dev/null +++ b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmCor= e.c @@ -0,0 +1,911 @@ +/** @file + This driver is responsible for the registration of child drivers + and the abstraction of the PCH SMI sources. + + Copyright (c) 2019 Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include "PchSmm.h" +#include "PchSmmHelpers.h" +#include "PchSmmEspi.h" +#include +#include +#include +#include + +// +// MODULE / GLOBAL DATA +// +// Module variables used by the both the main dispatcher and the source di= spatchers +// 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 PRIVATE_DATA mPrivateData =3D { + { + NULL, + NULL + }, // CallbackDataBase linked list he= ad + NULL, // EFI handle returned when callin= g 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] =3D { + { + 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 =3D TRUE; + + return EFI_SUCCESS; +} + +/** + PchSmiDispatcher SMM Module Entry Point\n + - Introduction\n + The PchSmiDispatcher module is an SMM driver which provides SMI handl= er registration + services for PCH generated SMIs. + + - Details\n + This module provides SMI handler registration servicies for PCH SMIs. + NOTE: All the register/unregister functions will be locked after SMM r= eady 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 Specificat= ion + - 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_PROT= OCOL @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 + - @link _PCH_ESPI_SMI_DISPATCH_PROTOCOL PCH_ESPI_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 complete= d. +**/ +EFI_STATUS +EFIAPI +InitializePchSmmDispatcher ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + VOID *SmmReadyToLockRegistration; + + // + // Access ACPI Base Addresses Register + // + + mAcpiBaseAddr =3D PmcGetAcpiBase (); + ASSERT (mAcpiBaseAddr !=3D 0); + + // + // Access TCO Base Addresses Register + // + PchTcoBaseGet (&mTcoBaseAddr); + ASSERT (mTcoBaseAddr !=3D 0); + + + // + // Register a callback function to handle subsequent SMIs. This callbac= k + // will be called by SmmCoreDispatcher. + // + Status =3D gSmst->SmiHandlerRegister (PchSmmCoreDispatcher, NULL, &mPriv= ateData.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 (); + InstallIoTrap (ImageHandle); + InstallEspiSmi (ImageHandle); + InstallPchSmmPeriodicTimerControlProtocol (mPrivateData.InstallMultProtH= andle); + + // + // Register EFI_SMM_READY_TO_LOCK_PROTOCOL_GUID notify function. + // + Status =3D 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 reg= ister. + + @retval EFI_INVALID_PARAMETER Error with NULL SMI source descrip= tion + @retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database= record + @retval EFI_SUCCESS The database record is created suc= cessfully. +**/ +EFI_STATUS +SmmCoreInsertRecord ( + IN DATABASE_RECORD *NewRecord, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + DATABASE_RECORD *Record; + + if ((NewRecord =3D=3D NULL) || + (NewRecord->Signature !=3D DATABASE_RECORD_SIGNATURE)) + { + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + Status =3D gSmst->SmmAllocatePool (EfiRuntimeServicesData, sizeof (DATAB= ASE_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 re= cord into the database + // + InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link); + + // + // Child's handle will be the address linked list link in the record + // + *DispatchHandle =3D (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 der= egister. + + @retval EFI_SUCCESS The dispatch function has been suc= cessfully + unregistered and the SMI source ha= s been disabled + if there are no other registered c= hild dispatch + functions for this SMI source. + @retval EFI_INVALID_PARAMETER Handle is invalid. + @retval EFI_ACCESS_DENIED Return access denied if the SmmRea= dyToLock 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 =3D QUALIFIED_PROTOCOL_FROM_GENERIC (This); + RecordToDelete =3D DATABASE_RECORD_FROM_LINK (DispatchHandle); + Status =3D PchSmmCoreUnRegister (NULL, DispatchHandle); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileUnregisterHandler (Qualified->Guid, RecordToDelete->C= allback, NULL, 0); + } + 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 invok= ed for this SMI source. + @param[in] DispatchContext Pointer to the dispatch function's conte= xt. + @param[out] DispatchHandle Handle of dispatch function, for when in= terfacing + 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 databas= e record + @retval EFI_INVALID_PARAMETER The input parameter is invalid + @retval EFI_SUCCESS The dispatch function has been successfu= lly + registered and the SMI source has been e= nabled. +**/ +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 =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the EndOfDxe event ha= s been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + ZeroMem (&Record, sizeof (DATABASE_RECORD)); + + // + // Gather information about the registration request + // + Record.Callback =3D DispatchFunction; + + Qualified =3D QUALIFIED_PROTOCOL_FROM_GENERIC (This); + + Record.ProtocolType =3D Qualified->Type; + + Record.ContextFunctions =3D mContextFunctions[Qualified->Type]; + // + // Perform linked list housekeeping + // + Record.Signature =3D 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 =3D 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.ChildConte= xt.Usb.Type > UsbWake)) { + return EFI_INVALID_PARAMETER; + } + + MapUsbToSrcDesc (DispatchContext, &Record.SrcDesc); + Record.ClearSource =3D NULL; + // + // use default clear source function + // + break; + + case SxType: + Record.ContextSize =3D 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 >=3D EfiMaximumSleepType) || + (Record.ChildContext.Sx.Phase < SxEntry) || + (Record.ChildContext.Sx.Phase >=3D EfiMaximumPhase) + ) { + return EFI_INVALID_PARAMETER; + } + + CopyMem (&Record.SrcDesc, &mSxSourceDesc, sizeof (PCH_SMM_SOURCE_DES= C)); + Record.ClearSource =3D NULL; + // + // use default clear source function + // + break; + + case PowerButtonType: + Record.ContextSize =3D 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_S= OURCE_DESC)); + Record.ClearSource =3D NULL; + // + // use default clear source function + // + break; + + case PeriodicTimerType: + Record.ContextSize =3D sizeof (EFI_SMM_PERIODIC_TIMER_REGISTER_CONTE= XT); + CopyMem (&Record.ChildContext, DispatchContext, Record.ContextSize); + // + // Check the validity of timer value + // + if (DispatchContext->PeriodicTimer.SmiTickInterval <=3D 0) { + return EFI_INVALID_PARAMETER; + } + + MapPeriodicTimerToSrcDesc (DispatchContext, &Record.SrcDesc); + Record.ClearSource =3D 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 re= cord into the database + // Child's handle will be the address linked list link in the record + // + Status =3D SmmCoreInsertRecord ( + &Record, + DispatchHandle + ); + ASSERT_EFI_ERROR (Status); + + if (Record.ClearSource =3D=3D 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, (UI= NTN)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 deregiste= r. + + @retval EFI_SUCCESS The dispatch function has been successfu= lly + unregistered and the SMI source has been= disabled + if there are no other registered child d= ispatch + 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 =3D=3D NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "UnRegister is not allowed if the SmmReadyToLock = event has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + RecordToDelete =3D DATABASE_RECORD_FROM_LINK (DispatchHandle); + + // + // Take the entry out of the linked list + // + if (RecordToDelete->Link.ForwardLink =3D=3D (LIST_ENTRY *) EFI_BAD_POINT= ER) { + return EFI_INVALID_PARAMETER; + } + + RemoveEntryList (&RecordToDelete->Link); + + // + // Loop through all the souces in record linked list to see if any sourc= e enable is equal. + // If any source enable is equal, we do not want to disable it. + // + for (DescIndex =3D 0; DescIndex < NUM_EN_BITS; ++DescIndex) { + if (IS_BIT_DESC_NULL (RecordToDelete->SrcDesc.En[DescIndex])) { + continue; + } + NeedClearEnable =3D TRUE; + LinkInDb =3D GetFirstNode (&mPrivateData.CallbackDataBase); + while (!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) { + RecordInDb =3D DATABASE_RECORD_FROM_LINK (LinkInDb); + if (IsBitEqualToAnySourceEn (&RecordToDelete->SrcDesc.En[DescIndex],= &RecordInDb->SrcDesc)) { + NeedClearEnable =3D FALSE; + break; + } + LinkInDb =3D GetNextNode (&mPrivateData.CallbackDataBase, &RecordInD= b->Link); + } + if (NeedClearEnable =3D=3D FALSE) { + continue; + } + WriteBitDesc (&RecordToDelete->SrcDesc.En[DescIndex], 0, FALSE); + } + Status =3D 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 hid= e 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_NE= WCENTURY) { + PchTcoSmiClearSourceAndBlock (&mSrcDescNewCentury); + } + } + // Clear PWRBTNOR_STS if it's not handled. + // + if (IoRead16 (mAcpiBaseAddr + R_ACPI_IO_PM1_STS) & B_ACPI_IO_PM1_STS_PRB= TNOR) { + IoWrite16 (mAcpiBaseAddr + R_ACPI_IO_PM1_STS, B_ACPI_IO_PM1_STS_PRBTNO= R); + } + // + // 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_G= PE0_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_U= NLOCK); + } +} + +/** + 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 =3D 3; + ContextsMatch =3D FALSE; + EosSet =3D FALSE; + SxChildWasDispatched =3D FALSE; + Status =3D 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 =3D IoRead8 (R_RTC_IO_EXT_INDEX_ALT); + Port74Save =3D IoRead8 (R_RTC_IO_INDEX_ALT); + + if (!IsListEmpty (&mPrivateData.CallbackDataBase)) { + // + // We have children registered w/ us -- continue + // + while ((!EosSet) && (EscapeCount > 0)) { + EscapeCount--; + + LinkInDb =3D GetFirstNode (&mPrivateData.CallbackDataBase); + + // + // Cache SciEn, SmiEnValue and SmiStsValue to determine if source is= active + // + SciEn =3D PchSmmGetSciEn (); + SmiEnValue =3D IoRead32 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_SMI_EN)= ); + SmiStsValue =3D IoRead32 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_SMI_STS= )); + + while (!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) { + RecordInDb =3D DATABASE_RECORD_FROM_LINK (LinkInDb); + + // + // look for the first active source + // + if (!SourceIsActive (&RecordInDb->SrcDesc, SciEn, SmiEnValue, SmiS= tsValue)) { + // + // Didn't find the source yet, keep looking + // + LinkInDb =3D GetNextNode (&mPrivateData.CallbackDataBase, &Recor= dInDb->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 =3D 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 =3D=3D SxType) { + SxChildWasDispatched =3D TRUE; + } + // + // "cache" the source description and don't query I/O anymore + // + CopyMem ((VOID *) &ActiveSource, (VOID *) &(RecordInDb->SrcDesc)= , sizeof (PCH_SMM_SOURCE_DESC)); + LinkToExhaust =3D LinkInDb; + + // + // exhaust the rest of the queue looking for the same source + // + while (!IsNull (&mPrivateData.CallbackDataBase, LinkToExhaust)) = { + RecordToExhaust =3D DATABASE_RECORD_FROM_LINK (LinkToExhaust); + // + // RecordToExhaust->Link might be removed (unregistered) by Ca= llback 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 =3D GetNextNode (&mPrivateData.CallbackDataBase,= &RecordToExhaust->Link); + + if (CompareSources (&RecordToExhaust->SrcDesc, &ActiveSource))= { + // + // These source descriptions are equal, so this callback sho= uld be + // dispatched. + // + if (RecordToExhaust->ContextFunctions.GetContext !=3D 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 !=3D = NULL); + + // + // Make sure contexts match before dispatching event to ch= ild + // + RecordToExhaust->ContextFunctions.GetContext (RecordToExha= ust, &Context); + ContextsMatch =3D RecordToExhaust->ContextFunctions.CmpCon= text (&Context, &RecordToExhaust->ChildContext); + + } else { + // + // This child doesn't require any more calling context bey= ond what + // it supplied in registration. Simply pass back what it = gave us. + // + Context =3D RecordToExhaust->ChildContext; + ContextsMatch =3D TRUE; + } + + if (ContextsMatch) { + if (RecordToExhaust->ProtocolType =3D=3D PchSmiDispatchTyp= e) { + // + // For PCH SMI dispatch protocols + // + PchSmiTypeCallbackDispatcher (RecordToExhaust); + } else { + // + // For EFI standard SMI dispatch protocols + // + if (RecordToExhaust->Callback !=3D NULL) { + if (RecordToExhaust->ContextFunctions.GetCommBuffer != =3D NULL) { + // + // This callback function needs CommBuffer and CommB= ufferSize. + // Get those from child and then pass to callback fu= nction. + // + RecordToExhaust->ContextFunctions.GetCommBuffer (Rec= ordToExhaust, &CommBuffer, &CommBufferSize); + } else { + // + // Child doesn't support the CommBuffer and CommBuff= erSize. + // Just pass NULL value to callback function. + // + CommBuffer =3D NULL; + CommBufferSize =3D 0; + } + + PERF_START_EX (NULL, "SmmFunction", NULL, AsmReadTsc (= ), RecordToExhaust->ProtocolType); + RecordToExhaust->Callback ((EFI_HANDLE) & RecordToExha= ust->Link, &Context, CommBuffer, &CommBufferSize); + PERF_END_EX (NULL, "SmmFunction", NULL, AsmReadTsc (),= RecordToExhaust->ProtocolType); + if (RecordToExhaust->ProtocolType =3D=3D SxType) { + SxChildWasDispatched =3D TRUE; + } + } else { + ASSERT (FALSE); + } + } + } + } + } + + if (RecordInDb->ClearSource =3D=3D NULL) { + // + // Clear the SMI associated w/ the source using the default fu= nction + // + 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 =3D PchSmmSetAndCheckEos (); + // + // Queue is empty, reset the search + // + break; + } + } + } + } + // + // If you arrive here, there are two possible reasons: + // (1) you've got problems with clearing the SMI status bits in the + // ACPI table. If you don't properly clear the SMI bits, then you won't= be able to set the + // EOS bit. If this happens too many times, the loop exits. + // (2) there was a SMM communicate for callback messages that was receiv= ed prior + // to this driver. + // If there is an asynchronous SMI that occurs while processing the Call= back, let + // all of the drivers (including this one) have an opportunity to scan f= or the SMI + // and handle it. + // If not, we don't want to exit and have the foreground app. clear EOS = without letting + // these other sources get serviced. + // + // This assert is not valid with CSM legacy solution because it generate= s software SMI + // to test for legacy USB support presence. + // This may not be illegal, so we cannot assert at this time. + // + // ASSERT (EscapeCount > 0); + // + if (SxChildWasDispatched) { + // + // A child of the SmmSxDispatch protocol was dispatched during this ca= ll; + // put the system to sleep. + // + PchSmmSxGoToSleep (); + } + // + // 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/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/Pc= hSmmEspi.c b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/Pc= hSmmEspi.c new file mode 100644 index 0000000000..9eb61947a3 --- /dev/null +++ b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmEsp= i.c @@ -0,0 +1,1595 @@ +/** @file + eSPI SMI implementation + + Copyright (c) 2019 Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include "PchSmmEspi.h" +#include +#include +#include +#include +#include +#include + +GLOBAL_REMOVE_IF_UNREFERENCED ESPI_SMI_INSTANCE mEspiSmiInstance =3D { + // + // Signature + // + ESPI_SMI_INSTANCE_SIGNATURE, + // + // Handle + // + NULL, + // + // PchEspiSmiDispatchProtocol + // + { + PCH_ESPI_SMI_DISPATCH_REVISION, + EspiSmiUnRegister, + BiosWrProtectRegister, + BiosWrReportRegister, + PcNonFatalErrRegister, + PcFatalErrRegister, + VwNonFatalErrRegister, + VwFatalErrRegister, + FlashNonFatalErrRegister, + FlashFatalErrRegister, + LnkType1ErrRegister, + EspiSlaveSmiRegister + }, + // + // PchSmiEspiHandle[EspiTopLevelTypeMax] + // + { + NULL, NULL, NULL + }, + // + // CallbackDataBase[EspiSmiTypeMax] + // + { + {NULL, NULL}, {NULL, NULL}, {NULL, NULL}, {NULL, NULL}, {NULL, NULL}, + {NULL, NULL}, {NULL, NULL}, {NULL, NULL}, {NULL, NULL}, {NULL, NULL} + }, + // + // EspiSmiEventCounter[EspiSmiTypeMax] + // + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, + // + // Barrier[EspiTopLevelTypeMax] + // + { + { + BiosWrProtect, + BiosWrProtect + }, + { + BiosWrReport, + LnkType1Err + }, + { + EspiSlaveSmi, + EspiSlaveSmi + } + } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED CONST ESPI_DESCRIPTOR mEspiDescriptor[EspiSm= iTypeMax] =3D { + // + // BiosWrProtect + // + { + { + PCIE_ADDR_TYPE, + { ( + (DEFAULT_PCI_BUS_NUMBER_PCH << 24) | + (PCI_DEVICE_NUMBER_PCH_LPC << 19) | + (PCI_FUNCTION_NUMBER_PCH_LPC << 16) | + R_ESPI_CFG_PCBC + ) } + }, + // + // SourceIsActiveAndMask and SourceIsActiveValue + // + B_ESPI_CFG_PCBC_BWPDS | B_ESPI_CFG_PCBC_LE, + B_ESPI_CFG_PCBC_BWPDS | B_ESPI_CFG_PCBC_LE, + // + // ClearStatusAndMask and ClearStatusOrMask + // + (UINT32) ~B_ESPI_CFG_PCBC_BWRS, + B_ESPI_CFG_PCBC_BWPDS + }, + // + // BiosWrReport + // + { + { + PCIE_ADDR_TYPE, + { ( + (DEFAULT_PCI_BUS_NUMBER_PCH << 24) | + (PCI_DEVICE_NUMBER_PCH_LPC << 19) | + (PCI_FUNCTION_NUMBER_PCH_LPC << 16) | + R_ESPI_CFG_PCBC + ) } + }, + B_ESPI_CFG_PCBC_BWRS | B_ESPI_CFG_PCBC_BWRE, + B_ESPI_CFG_PCBC_BWRS | B_ESPI_CFG_PCBC_BWRE, + (UINT32) ~B_ESPI_CFG_PCBC_BWPDS, + B_ESPI_CFG_PCBC_BWRS + }, + // + // PcNonFatalErr + // + { + { + PCR_ADDR_TYPE, + {PCH_PCR_ADDRESS (PID_ESPISPI, R_ESPI_PCR_PCERR_SLV0) } + }, + (B_ESPI_PCR_XERR_XNFES | B_ESPI_PCR_XERR_XNFEE), + (B_ESPI_PCR_XERR_XNFES | (V_ESPI_PCR_XERR_XNFEE_SMI << N_ESPI_PCR_XERR= _XNFEE)), + (UINT32) ~(B_ESPI_PCR_PCERR_SLV0_PCURD | B_ESPI_PCR_XERR_XFES), + B_ESPI_PCR_XERR_XNFES + }, + // + // PcFatalErr + // + { + { + PCR_ADDR_TYPE, + {PCH_PCR_ADDRESS (PID_ESPISPI, R_ESPI_PCR_PCERR_SLV0) } + }, + (B_ESPI_PCR_XERR_XFES | B_ESPI_PCR_XERR_XFEE), + (B_ESPI_PCR_XERR_XFES | (V_ESPI_PCR_XERR_XFEE_SMI << N_ESPI_PCR_XERR_X= FEE)), + (UINT32) ~(B_ESPI_PCR_PCERR_SLV0_PCURD | B_ESPI_PCR_XERR_XNFES), + B_ESPI_PCR_XERR_XFES + }, + // + // VwNonFatalErr + // + { + { + PCR_ADDR_TYPE, + {PCH_PCR_ADDRESS (PID_ESPISPI, R_ESPI_PCR_VWERR_SLV0) } + }, + (B_ESPI_PCR_XERR_XNFES | B_ESPI_PCR_XERR_XNFEE), + (B_ESPI_PCR_XERR_XNFES | (V_ESPI_PCR_XERR_XNFEE_SMI << N_ESPI_PCR_XERR= _XNFEE)), + (UINT32) ~B_ESPI_PCR_XERR_XFES, + B_ESPI_PCR_XERR_XNFES + }, + // + // VwFatalErr + // + { + { + PCR_ADDR_TYPE, + {PCH_PCR_ADDRESS (PID_ESPISPI, R_ESPI_PCR_VWERR_SLV0) } + }, + (B_ESPI_PCR_XERR_XFES | B_ESPI_PCR_XERR_XFEE), + (B_ESPI_PCR_XERR_XFES | (V_ESPI_PCR_XERR_XFEE_SMI << N_ESPI_PCR_XERR_X= FEE)), + (UINT32) ~B_ESPI_PCR_XERR_XNFES, + B_ESPI_PCR_XERR_XFES + }, + // + // FlashNonFatalErr + // + { + { + PCR_ADDR_TYPE, + {PCH_PCR_ADDRESS (PID_ESPISPI, R_ESPI_PCR_FCERR_SLV0) } + }, + (B_ESPI_PCR_XERR_XNFES | B_ESPI_PCR_XERR_XNFEE), + (B_ESPI_PCR_XERR_XNFES | (V_ESPI_PCR_XERR_XNFEE_SMI << N_ESPI_PCR_XERR= _XNFEE)), + (UINT32) ~B_ESPI_PCR_XERR_XFES, + B_ESPI_PCR_XERR_XNFES + }, + // + // FlashFatalErr + // + { + { + PCR_ADDR_TYPE, + {PCH_PCR_ADDRESS (PID_ESPISPI, R_ESPI_PCR_FCERR_SLV0) } + }, + (B_ESPI_PCR_XERR_XFES | B_ESPI_PCR_XERR_XFEE), + (B_ESPI_PCR_XERR_XFES | (V_ESPI_PCR_XERR_XFEE_SMI << N_ESPI_PCR_XERR_X= FEE)), + (UINT32) ~B_ESPI_PCR_XERR_XNFES, + B_ESPI_PCR_XERR_XFES + }, + // + // LnkType1Err + // + { + { + PCR_ADDR_TYPE, + {PCH_PCR_ADDRESS (PID_ESPISPI, R_ESPI_PCR_LNKERR_SLV0) } + }, + B_ESPI_PCR_LNKERR_SLV0_LFET1S | B_ESPI_PCR_LNKERR_SLV0_LFET1E, + B_ESPI_PCR_LNKERR_SLV0_LFET1S | (V_ESPI_PCR_LNKERR_SLV0_LFET1E_SMI << = N_ESPI_PCR_LNKERR_SLV0_LFET1E), + (UINT32) ~B_ESPI_PCR_LNKERR_SLV0_SLCRR, + B_ESPI_PCR_LNKERR_SLV0_LFET1S + }, +}; + +/** + Enable eSPI SMI source + + @param[in] EspiSmiType Type based on ESPI_SMI_TYPE +**/ +STATIC +VOID +EspiSmiEnableSource ( + IN CONST ESPI_SMI_TYPE EspiSmiType + ) +{ + UINT64 PciBaseAddress; + + switch (EspiSmiType) { + case BiosWrProtect: + // + // It doesn't enable the BIOSLOCK here. Enable it by policy in DXE. + // + break; + case BiosWrReport: + PciBaseAddress =3D PCI_SEGMENT_LIB_ADDRESS ( + DEFAULT_PCI_SEGMENT_NUMBER_PCH, + DEFAULT_PCI_BUS_NUMBER_PCH, + PCI_DEVICE_NUMBER_PCH_LPC, + PCI_FUNCTION_NUMBER_PCH_LPC, + 0 + ); + PciSegmentAndThenOr32 ( + PciBaseAddress + R_ESPI_CFG_PCBC, + (UINT32) ~(B_ESPI_CFG_PCBC_BWRS | B_ESPI_CFG_PCBC_BWPDS), + B_ESPI_CFG_PCBC_BWRE + ); + break; + case PcNonFatalErr: + PchPcrAndThenOr32 ( + PID_ESPISPI, + (UINT16) R_ESPI_PCR_PCERR_SLV0, + (UINT32) ~(B_ESPI_PCR_PCERR_SLV0_PCURD | B_ESPI_PCR_XERR_XNFES | B= _ESPI_PCR_XERR_XFES), + B_ESPI_PCR_XERR_XNFEE + ); + break; + + case PcFatalErr: + PchPcrAndThenOr32 ( + PID_ESPISPI, + (UINT16) R_ESPI_PCR_PCERR_SLV0, + (UINT32) ~(B_ESPI_PCR_PCERR_SLV0_PCURD | B_ESPI_PCR_XERR_XNFES | B= _ESPI_PCR_XERR_XFES), + B_ESPI_PCR_XERR_XFEE + ); + break; + + case VwNonFatalErr: + PchPcrAndThenOr32 ( + PID_ESPISPI, + (UINT16) R_ESPI_PCR_VWERR_SLV0, + (UINT32) ~(B_ESPI_PCR_XERR_XNFES | B_ESPI_PCR_XERR_XFES), + B_ESPI_PCR_XERR_XNFEE + ); + break; + + case VwFatalErr: + PchPcrAndThenOr32 ( + PID_ESPISPI, + (UINT16) R_ESPI_PCR_VWERR_SLV0, + (UINT32) ~(B_ESPI_PCR_XERR_XNFES | B_ESPI_PCR_XERR_XFES), + B_ESPI_PCR_XERR_XFEE + ); + break; + + case FlashNonFatalErr: + PchPcrAndThenOr32 ( + PID_ESPISPI, + (UINT16) R_ESPI_PCR_FCERR_SLV0, + (UINT32) ~(B_ESPI_PCR_XERR_XNFES | B_ESPI_PCR_XERR_XFES), + B_ESPI_PCR_XERR_XNFEE + ); + break; + + case FlashFatalErr: + PchPcrAndThenOr32 ( + PID_ESPISPI, + (UINT16) R_ESPI_PCR_FCERR_SLV0, + (UINT32) ~(B_ESPI_PCR_XERR_XNFES | B_ESPI_PCR_XERR_XFES), + B_ESPI_PCR_XERR_XFEE + ); + break; + + case LnkType1Err: + PchPcrAndThenOr32 ( + PID_ESPISPI, + (UINT16) R_ESPI_PCR_LNKERR_SLV0, + (UINT32) ~(B_ESPI_PCR_LNKERR_SLV0_SLCRR | B_ESPI_PCR_LNKERR_SLV0_L= FET1S), + (UINT32) (V_ESPI_PCR_LNKERR_SLV0_LFET1E_SMI << N_ESPI_PCR_LNKERR_S= LV0_LFET1E) + ); + + if (IsEspiSecondSlaveSupported ()) { + PchPcrAndThenOr32 ( + PID_ESPISPI, + (UINT16) R_ESPI_PCR_LNKERR_SLV1, + (UINT32) ~(B_ESPI_PCR_LNKERR_SLV0_SLCRR | B_ESPI_PCR_LNKERR_SLV0= _LFET1S), + (UINT32) (V_ESPI_PCR_LNKERR_SLV0_LFET1E_SMI << N_ESPI_PCR_LNKERR= _SLV0_LFET1E) + ); + } + break; + + default: + DEBUG ((DEBUG_ERROR, "Unsupported EspiSmiType \n")); + ASSERT (FALSE); + break; + } +} + + +/** + Disable eSPI SMI source + + @param[in] EspiSmiType Type based on ESPI_SMI_TYPE +**/ +STATIC +VOID +EspiSmiDisableSource ( + IN CONST ESPI_SMI_TYPE EspiSmiType + ) +{ + + switch (EspiSmiType) { + case BiosWrProtect: + case BiosWrReport: + DEBUG ((DEBUG_ERROR, "Bit is write lock, thus BWRE/BWPDS source cann= ot be disabled \n")); + ASSERT (FALSE); + break; + case PcNonFatalErr: + PchPcrAndThenOr32 ( + PID_ESPISPI, + (UINT16) R_ESPI_PCR_PCERR_SLV0, + (UINT32) ~(B_ESPI_PCR_PCERR_SLV0_PCURD | B_ESPI_PCR_XERR_XNFES | B= _ESPI_PCR_XERR_XFES | B_ESPI_PCR_XERR_XNFEE), + 0 + ); + break; + + case PcFatalErr: + PchPcrAndThenOr32 ( + PID_ESPISPI, + (UINT16) R_ESPI_PCR_PCERR_SLV0, + (UINT32) ~(B_ESPI_PCR_PCERR_SLV0_PCURD | B_ESPI_PCR_XERR_XNFES | B= _ESPI_PCR_XERR_XFES | B_ESPI_PCR_XERR_XFEE), + 0 + ); + break; + + case VwNonFatalErr: + PchPcrAndThenOr32 ( + PID_ESPISPI, + (UINT16) R_ESPI_PCR_VWERR_SLV0, + (UINT32) ~(B_ESPI_PCR_XERR_XNFES | B_ESPI_PCR_XERR_XFES | B_ESPI_P= CR_XERR_XNFEE), + 0 + ); + break; + + case VwFatalErr: + PchPcrAndThenOr32 ( + PID_ESPISPI, + (UINT16) R_ESPI_PCR_VWERR_SLV0, + (UINT32) ~(B_ESPI_PCR_XERR_XNFES | B_ESPI_PCR_XERR_XFES | B_ESPI_P= CR_XERR_XFEE), + 0 + ); + break; + + case FlashNonFatalErr: + PchPcrAndThenOr32 ( + PID_ESPISPI, + (UINT16) R_ESPI_PCR_FCERR_SLV0, + (UINT32) ~(B_ESPI_PCR_XERR_XNFES | B_ESPI_PCR_XERR_XFES | B_ESPI_P= CR_XERR_XNFEE), + 0 + ); + break; + + case FlashFatalErr: + PchPcrAndThenOr32 ( + PID_ESPISPI, + (UINT16) R_ESPI_PCR_FCERR_SLV0, + (UINT32) ~(B_ESPI_PCR_XERR_XNFES | B_ESPI_PCR_XERR_XFES | B_ESPI_PC= R_XERR_XFEE), + 0 + ); + break; + + case LnkType1Err: + PchPcrAndThenOr32 ( + PID_ESPISPI, + (UINT16) R_ESPI_PCR_LNKERR_SLV0, + (UINT32) ~(B_ESPI_PCR_LNKERR_SLV0_SLCRR | B_ESPI_PCR_LNKERR_SLV0_L= FET1S), + 0 + ); + + if (IsEspiSecondSlaveSupported ()) { + PchPcrAndThenOr32 ( + PID_ESPISPI, + (UINT16) R_ESPI_PCR_LNKERR_SLV1, + (UINT32) ~(B_ESPI_PCR_LNKERR_SLV0_SLCRR | B_ESPI_PCR_LNKERR_SLV0= _LFET1S), + 0 + ); + } + break; + + default: + DEBUG ((DEBUG_ERROR, "Unsupported EspiSmiType \n")); + ASSERT (FALSE); + break; + } +} + +/** + Clear a status for the SMI event + + @param[in] EspiSmiType Type based on ESPI_SMI_TYPE +**/ +STATIC +VOID +EspiSmiClearStatus ( + IN CONST ESPI_SMI_TYPE EspiSmiType + ) +{ + UINT32 PciBus; + UINT32 PciDev; + UINT32 PciFun; + UINT32 PciReg; + UINT64 PciBaseAddress; + CONST ESPI_DESCRIPTOR *Desc; + + Desc =3D &mEspiDescriptor[EspiSmiType]; + + switch (Desc->Address.Type) { + case PCIE_ADDR_TYPE: + PciBus =3D Desc->Address.Data.pcie.Fields.Bus; + PciDev =3D Desc->Address.Data.pcie.Fields.Dev; + PciFun =3D Desc->Address.Data.pcie.Fields.Fnc; + PciReg =3D Desc->Address.Data.pcie.Fields.Reg; + PciBaseAddress =3D PCI_SEGMENT_LIB_ADDRESS (DEFAULT_PCI_SEGMENT_NUMB= ER_PCH, PciBus, PciDev, PciFun, 0); + PciSegmentAndThenOr32 (PciBaseAddress + PciReg, Desc->ClearStatusAnd= Mask, Desc->ClearStatusOrMask); + break; + case PCR_ADDR_TYPE: + PchPcrAndThenOr32 ( + Desc->Address.Data.Pcr.Fields.Pid, + Desc->Address.Data.Pcr.Fields.Offset, + Desc->ClearStatusAndMask, + Desc->ClearStatusOrMask + ); + break; + default: + DEBUG ((DEBUG_ERROR, "Address type for eSPI SMI is invalid \n")); + ASSERT (FALSE); + break; + } +} + +/** + Checks if a source is active by looking at the enable and status bits + + @param[in] EspiSmiType Type based on ESPI_SMI_TYPE +**/ +STATIC +BOOLEAN +EspiSmiSourceIsActive ( + IN CONST ESPI_SMI_TYPE EspiSmiType + ) +{ + BOOLEAN Active; + UINT32 PciBus; + UINT32 PciDev; + UINT32 PciFun; + UINT32 PciReg; + UINT64 PciBaseAddress; + UINT32 Data32; + CONST ESPI_DESCRIPTOR *Desc; + + Desc =3D &mEspiDescriptor[EspiSmiType]; + + Active =3D FALSE; + switch (Desc->Address.Type) { + case PCIE_ADDR_TYPE: + PciBus =3D Desc->Address.Data.pcie.Fields.Bus; + PciDev =3D Desc->Address.Data.pcie.Fields.Dev; + PciFun =3D Desc->Address.Data.pcie.Fields.Fnc; + PciReg =3D Desc->Address.Data.pcie.Fields.Reg; + PciBaseAddress =3D PCI_SEGMENT_LIB_ADDRESS (DEFAULT_PCI_SEGMENT_NUMB= ER_PCH, PciBus, PciDev, PciFun, 0); + Data32 =3D PciSegmentRead32 (PciBaseAddress + PciReg); + break; + + case PCR_ADDR_TYPE: + Data32 =3D PchPcrRead32 ( + Desc->Address.Data.Pcr.Fields.Pid, + Desc->Address.Data.Pcr.Fields.Offset + ); + break; + + default: + Data32 =3D 0; + DEBUG ((DEBUG_ERROR, "Address type for eSPI SMI is invalid \n")); + ASSERT (FALSE); + break; + } + + if ((Data32 & Desc->SourceIsActiveAndMask) =3D=3D Desc->SourceIsActiveVa= lue) { + Active =3D TRUE; + } + + return Active; +} + +/** + Insert a handler into the corresponding linked list based on EspiSmiType + + @param[in] DispatchFunction The callback to execute + @param[in] EspiSmiType Type based on ESPI_SMI_TYPE to determi= ne which linked list to use + @param[out] DispatchHandle The link to the record in the database + + @retval EFI_SUCCESS Record was successfully inserted into = master database + @retval EFI_OUT_OF_RESOURCES Cannot allocate pool to insert record +**/ +STATIC +EFI_STATUS +InsertEspiRecord ( + IN PCH_ESPI_SMI_DISPATCH_CALLBACK DispatchFunction, + IN ESPI_SMI_TYPE EspiSmiType, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + ESPI_SMI_RECORD *Record; + + Status =3D gSmst->SmmAllocatePool (EfiRuntimeServicesData, sizeof (ESPI_= SMI_RECORD), (VOID **) &Record); + if (EFI_ERROR (Status)) { + ASSERT (FALSE); + return EFI_OUT_OF_RESOURCES; + } + SetMem (Record, sizeof (ESPI_SMI_RECORD), 0); + + Record->Callback =3D DispatchFunction; + Record->Signature =3D ESPI_SMI_RECORD_SIGNATURE; + + InsertTailList (&mEspiSmiInstance.CallbackDataBase[EspiSmiType], &Record= ->Link); + EspiSmiClearStatus (EspiSmiType); + EspiSmiEnableSource (EspiSmiType); + + ++mEspiSmiInstance.EspiSmiEventCounter[EspiSmiType]; + + *DispatchHandle =3D (EFI_HANDLE) (&Record->Link); + + return EFI_SUCCESS; +} + +/** + This callback is registered to PchSmiDispatch + + @param[in] DispatchHandle Used to determine which source have been tri= ggered +**/ +VOID +EspiSmiCallback ( + IN EFI_HANDLE DispatchHandle + ) +{ + DATABASE_RECORD *PchSmiRecord; + ESPI_TOP_LEVEL_TYPE EspiTopLevelType; + ESPI_SMI_TYPE EspiSmiType; + ESPI_SMI_RECORD *RecordInDb; + LIST_ENTRY *LinkInDb; + + PchSmiRecord =3D DATABASE_RECORD_FROM_LINK (DispatchHandle); + + if (PchSmiRecord->PchSmiType =3D=3D PchTcoSmiLpcBiosWpType) { + EspiTopLevelType =3D EspiBiosWrProtect; + } else if (PchSmiRecord->PchSmiType =3D=3D PchSmiSerialIrqType) { + EspiTopLevelType =3D EspiSerialIrq; + } else { + DEBUG ((DEBUG_ERROR, "EspiSmiCallback was dispatched with a wrong Disp= atchHandle")); + ASSERT (FALSE); + return; + } + + for (EspiSmiType =3D mEspiSmiInstance.Barrier[EspiTopLevelType].Start; E= spiSmiType <=3D mEspiSmiInstance.Barrier[EspiTopLevelType].End; ++EspiSmiTy= pe) { + if (!EspiSmiSourceIsActive (EspiSmiType)) { + continue; + } + // + // The source is active, so walk the callback database and dispatch + // + if (!IsListEmpty (&mEspiSmiInstance.CallbackDataBase[EspiSmiType])) { + // + // We have children registered w/ us -- continue + // + LinkInDb =3D GetFirstNode (&mEspiSmiInstance.CallbackDataBase[EspiSm= iType]); + + while (!IsNull (&mEspiSmiInstance.CallbackDataBase[EspiSmiType], Lin= kInDb)) { + RecordInDb =3D ESPI_RECORD_FROM_LINK (LinkInDb); + + // + // RecordInDb->Link might be removed (unregistered) by Callback fu= nction, and then the + // system will hang in ASSERT() while calling GetNextNode(). + // To prevent the issue, we need to get next record in DB here (be= fore Callback function). + // + LinkInDb =3D GetNextNode (&mEspiSmiInstance.CallbackDataBase[EspiS= miType], &RecordInDb->Link); + + // + // Callback + // + if (RecordInDb->Callback !=3D NULL) { + RecordInDb->Callback ((EFI_HANDLE) &RecordInDb->Link); + } else { + ASSERT (FALSE); + } + } + } else if (EspiSmiType =3D=3D LnkType1Err) { + // + // If no proper handler registered for Link Type 1 Error + // Call default SMI handler recover otherwise + // + EspiDefaultFatalErrorHandler (); + } + + // + // Finish walking the linked list for the EspiSmiType, so clear status + // + EspiSmiClearStatus (EspiSmiType); + } +} + +// +// EspiBiosWp srcdesc +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSrcDescEspiBiosWp= =3D { + 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, + { ( + (DEFAULT_PCI_BUS_NUMBER_PCH << 24) | + (PCI_DEVICE_NUMBER_PCH_LPC << 19) | + (PCI_FUNCTION_NUMBER_PCH_LPC << 16) | + R_ESPI_CFG_PCBC + ) } + }, + S_ESPI_CFG_PCBC, + N_ESPI_CFG_PCBC_LE + } + }, + { + { + { + PCIE_ADDR_TYPE, + { ( + (DEFAULT_PCI_BUS_NUMBER_PCH << 24) | + (PCI_DEVICE_NUMBER_PCH_LPC << 19) | + (PCI_FUNCTION_NUMBER_PCH_LPC << 16) | + R_ESPI_CFG_PCBC + ) } + }, + S_ESPI_CFG_PCBC, + N_ESPI_CFG_PCBC_BWPDS + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_TCO + } +}; + +/** + This function will register EspiSmiCallback with mSrcDescEspiBiosWp sour= ce decriptor + This function make sure there is only one BIOS WP SMI handler is registe= red. + While any ESPI sub BIOS WP SMI type is registered, all the BIOS WP SMI + will go to callback function EspiSmiCallback first, and then dispatchs t= he callbacks + recorded in mEspiSmiInstance. + + @retval EFI_SUCCESS Registration succeed + @retval others Registration failed +**/ +STATIC +EFI_STATUS +RegisterBiosWrProtectIfNull ( + VOID + ) +{ + EFI_STATUS Status; + DATABASE_RECORD *Record; + + if (mEspiSmiInstance.PchSmiEspiHandle[EspiBiosWrProtect] =3D=3D NULL) { + Status =3D PchSmiRecordInsert ( + &mSrcDescEspiBiosWp, + (PCH_SMI_CALLBACK_FUNCTIONS) EspiSmiCallback, + PchTcoSmiLpcBiosWpType, + &mEspiSmiInstance.PchSmiEspiHandle[EspiBiosWrProtect] + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Fail to register BIOS WP SMI handler \n")); + return Status; + } + Record =3D DATABASE_RECORD_FROM_LINK (mEspiSmiInstance.PchSmiEspiHandl= e[EspiBiosWrProtect]); + Record->ClearSource =3D PchTcoSmiClearSource; + } + + return EFI_SUCCESS; +} + +/** + This function will register EspiSmiCallback with mSrcDescSerialIrq sourc= e decriptor + This function make sure there is only one Serial IRQ SMI handler is regi= stered. + While any ESPI sub Serial IRQ SMI type is registered, all the Serial IRQ= SMI + will go to callback function EspiSmiCallback first, and then dispatchs t= he callbacks + recorded in mEspiSmiInstance. + + @retval EFI_SUCCESS Registration succeed + @retval others Registration failed +**/ +STATIC +EFI_STATUS +RegisterSerialIrqIfNull ( + VOID + ) +{ + EFI_STATUS Status; + + if (mEspiSmiInstance.PchSmiEspiHandle[EspiSerialIrq] =3D=3D NULL) { + Status =3D PchSmiRecordInsert ( + &mSrcDescSerialIrq, + (PCH_SMI_CALLBACK_FUNCTIONS) EspiSmiCallback, + PchSmiSerialIrqType, + &mEspiSmiInstance.PchSmiEspiHandle[EspiSerialIrq] + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Fail to register Serial IRQ SMI handler \n")); + return Status; + } + } + + return EFI_SUCCESS; +} + +/** + Installs and initialize this protocol + + @param[in] ImageHandle Not used + + @retval EFI_SUCCESS Installation succeed + @retval others Installation failed +**/ +EFI_STATUS +EFIAPI +InstallEspiSmi ( + IN EFI_HANDLE ImageHandle + ) +{ + EFI_STATUS Status; + ESPI_SMI_TYPE EspiSmiType; + + DEBUG ((DEBUG_INFO, "[InstallEspiSmi] Enter\n")); + + // + // InitializeListHead for mEspiSmiInstance.CallBackDataBase[EspiTopLevel= TypeMax] + // + for (EspiSmiType =3D 0; EspiSmiType < EspiSmiTypeMax; ++EspiSmiType) { + InitializeListHead (&mEspiSmiInstance.CallbackDataBase[EspiSmiType]); + } + + // + // Install EfiEspiSmiDispatchProtocol + // + Status =3D gSmst->SmmInstallProtocolInterface ( + &mEspiSmiInstance.Handle, + &gPchEspiSmiDispatchProtocolGuid, + EFI_NATIVE_INTERFACE, + &mEspiSmiInstance.PchEspiSmiDispatchProtocol + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "Failed to install eSPI SMI Dispatch Protocol\n")= ); + ASSERT (FALSE); + return Status; + } + + // Register eSPI SMM callback to enable Fatal Error handling by default = handler + Status =3D RegisterSerialIrqIfNull (); + if (EFI_ERROR (Status)) { + return Status; + } + + // Enable LnkType1Err SMI generation for default handler + EspiSmiClearStatus (LnkType1Err); + EspiSmiEnableSource (LnkType1Err); + + return EFI_SUCCESS; +} + +/** + eSPI SMI Dispatch Protocol instance to register a BIOS Write Protect eve= nt + + @param[in] This Not used + @param[in] DispatchFunction The callback to execute + @param[out] DispatchHandle The handle for this callback registration + + @retval EFI_SUCCESS Registration succeed + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock= event has been triggered + @retval others Registration failed +**/ +EFI_STATUS +EFIAPI +BiosWrProtectRegister ( + IN PCH_ESPI_SMI_DISPATCH_PROTOCOL *This, + IN PCH_ESPI_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock ev= ent has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status =3D RegisterBiosWrProtectIfNull (); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Insert a record + // + Status =3D InsertEspiRecord (DispatchFunction, BiosWrProtect, DispatchHa= ndle); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileRegisterHandler (&gPchEspiSmiDispatchProtocolGuid, (E= FI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NU= LL, 0); + } + return Status; +} + +/** + eSPI SMI Dispatch Protocol instance to register a BIOS Write Report even= t + + @param[in] This Not used + @param[in] DispatchFunction The callback to execute + @param[out] DispatchHandle The handle for this callback registration + + @retval EFI_SUCCESS Registration succeed + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock= event has been triggered + @retval others Registration failed +**/ +EFI_STATUS +EFIAPI +BiosWrReportRegister ( + IN PCH_ESPI_SMI_DISPATCH_PROTOCOL *This, + IN PCH_ESPI_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock ev= ent has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status =3D RegisterSerialIrqIfNull (); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Insert a record + // + Status =3D InsertEspiRecord (DispatchFunction, BiosWrReport, DispatchHan= dle); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileRegisterHandler (&gPchEspiSmiDispatchProtocolGuid, (E= FI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NU= LL, 0); + } + return Status; +} + +/** + eSPI SMI Dispatch Protocol instance to register a Peripheral Channel Non= Fatal Error event + + @param[in] This Not used + @param[in] DispatchFunction The callback to execute + @param[out] DispatchHandle The handle for this callback registration + + @retval EFI_SUCCESS Registration succeed + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock= event has been triggered + @retval others Registration failed +**/ +EFI_STATUS +EFIAPI +PcNonFatalErrRegister ( + IN PCH_ESPI_SMI_DISPATCH_PROTOCOL *This, + IN PCH_ESPI_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock ev= ent has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status =3D RegisterSerialIrqIfNull (); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Insert a record + // + Status =3D InsertEspiRecord (DispatchFunction, PcNonFatalErr, DispatchHa= ndle); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileRegisterHandler (&gPchEspiSmiDispatchProtocolGuid, (E= FI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NU= LL, 0); + } + return Status; +} + +/** + eSPI SMI Dispatch Protocol instance to register a Peripheral Channel Fat= al Error event + + @param[in] This Not used + @param[in] DispatchFunction The callback to execute + @param[out] DispatchHandle The handle for this callback registration + + @retval EFI_SUCCESS Registration succeed + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock= event has been triggered + @retval others Registration failed +**/ +EFI_STATUS +EFIAPI +PcFatalErrRegister ( + IN PCH_ESPI_SMI_DISPATCH_PROTOCOL *This, + IN PCH_ESPI_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock ev= ent has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status =3D RegisterSerialIrqIfNull (); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Insert a record + // + Status =3D InsertEspiRecord (DispatchFunction, PcFatalErr, DispatchHandl= e); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileRegisterHandler (&gPchEspiSmiDispatchProtocolGuid, (E= FI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NU= LL, 0); + } + return Status; +} + +/** + eSPI SMI Dispatch Protocol instance to register a Virtual Wire Non Fatal= Error event + + @param[in] This Not used + @param[in] DispatchFunction The callback to execute + @param[out] DispatchHandle The handle for this callback registration + + @retval EFI_SUCCESS Registration succeed + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock= event has been triggered + @retval others Registration failed +**/ +EFI_STATUS +EFIAPI +VwNonFatalErrRegister ( + IN PCH_ESPI_SMI_DISPATCH_PROTOCOL *This, + IN PCH_ESPI_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock ev= ent has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status =3D RegisterSerialIrqIfNull (); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Insert a record + // + Status =3D InsertEspiRecord (DispatchFunction, VwNonFatalErr, DispatchHa= ndle); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileRegisterHandler (&gPchEspiSmiDispatchProtocolGuid, (E= FI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NU= LL, 0); + } + return Status; +} + +/** + eSPI SMI Dispatch Protocol instance to register a Virtual Wire Fatal Err= or event + + @param[in] This Not used + @param[in] DispatchFunction The callback to execute + @param[out] DispatchHandle The handle for this callback registration + + @retval EFI_SUCCESS Registration succeed + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock= event has been triggered + @retval others Registration failed +**/ +EFI_STATUS +EFIAPI +VwFatalErrRegister ( + IN PCH_ESPI_SMI_DISPATCH_PROTOCOL *This, + IN PCH_ESPI_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock ev= ent has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status =3D RegisterSerialIrqIfNull (); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Insert a record + // + Status =3D InsertEspiRecord (DispatchFunction, VwFatalErr, DispatchHandl= e); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileRegisterHandler (&gPchEspiSmiDispatchProtocolGuid, (E= FI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NU= LL, 0); + } + return Status; +} + +/** + eSPI SMI Dispatch Protocol instance to register a Flash Channel Non Fata= l Error event + + @param[in] This Not used + @param[in] DispatchFunction The callback to execute + @param[out] DispatchHandle The handle for this callback registration + + @retval EFI_SUCCESS Registration succeed + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock= event has been triggered + @retval others Registration failed +**/ +EFI_STATUS +EFIAPI +FlashNonFatalErrRegister ( + IN PCH_ESPI_SMI_DISPATCH_PROTOCOL *This, + IN PCH_ESPI_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock ev= ent has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status =3D RegisterSerialIrqIfNull (); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Insert a record + // + Status =3D InsertEspiRecord (DispatchFunction, FlashNonFatalErr, Dispatc= hHandle); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileRegisterHandler (&gPchEspiSmiDispatchProtocolGuid, (E= FI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NU= LL, 0); + } + return Status; +} + +/** + eSPI SMI Dispatch Protocol instance to register a Flash Channel Fatal Er= ror event + + @param[in] This Not used + @param[in] DispatchFunction The callback to execute + @param[out] DispatchHandle The handle for this callback registration + + @retval EFI_SUCCESS Registration succeed + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock= event has been triggered + @retval others Registration failed +**/ +EFI_STATUS +EFIAPI +FlashFatalErrRegister ( + IN PCH_ESPI_SMI_DISPATCH_PROTOCOL *This, + IN PCH_ESPI_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock ev= ent has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status =3D RegisterSerialIrqIfNull (); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Insert a record + // + Status =3D InsertEspiRecord (DispatchFunction, FlashFatalErr, DispatchHa= ndle); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileRegisterHandler (&gPchEspiSmiDispatchProtocolGuid, (E= FI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NU= LL, 0); + } + return Status; +} + +/** + eSPI SMI Dispatch Protocol instance to register a Link Error event + + @param[in] This Not used + @param[in] DispatchFunction The callback to execute + @param[out] DispatchHandle The handle for this callback registration + + @retval EFI_SUCCESS Registration succeed + @retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock= event has been triggered + @retval others Registration failed +**/ +EFI_STATUS +EFIAPI +LnkType1ErrRegister ( + IN PCH_ESPI_SMI_DISPATCH_PROTOCOL *This, + IN PCH_ESPI_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock ev= ent has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status =3D RegisterSerialIrqIfNull (); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Insert a record + // + Status =3D InsertEspiRecord (DispatchFunction, LnkType1Err, DispatchHand= le); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileRegisterHandler (&gPchEspiSmiDispatchProtocolGuid, (E= FI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NU= LL, 0); + } + return Status; +} + +// +// EspiSlave srcdesc +// +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSrcDescEspiSlave = =3D { + PCH_SMM_NO_FLAGS, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_EN} + }, + S_ACPI_IO_SMI_EN, + N_ACPI_IO_SMI_EN_ESPI + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_ESPI + } + }, + { + { + ACPI_ADDR_TYPE, + {R_ACPI_IO_SMI_STS} + }, + S_ACPI_IO_SMI_STS, + N_ACPI_IO_SMI_STS_ESPI + } +}; + +/** + eSPI SMI Dispatch Protocol instance to register a eSPI slave SMI + This routine will also lock down ESPI_SMI_LOCK bit after registration an= d prevent + this handler from unregistration. + On platform that supports more than 1 device through another chip select= (SPT-H), + the SMI handler itself needs to inspect both the eSPI devices' interrupt= status registers + (implementation specific for each Slave) in order to identify and servic= e the cause. + After servicing it, it has to clear the Slaves' internal SMI# status reg= isters + + @param[in] This Not used + @param[in] DispatchFunction The callback to execute + @param[out] DispatchHandle The handle for this callback regis= tration + + @retval EFI_SUCCESS Registration succeed + @retval EFI_ACCESS_DENIED Return access denied if the SmmRea= dyToLock event has been triggered + @retval EFI_ACCESS_DENIED The ESPI_SMI_LOCK is set and regis= ter is blocked. + @retval others Registration failed +**/ +EFI_STATUS +EFIAPI +EspiSlaveSmiRegister ( + IN PCH_ESPI_SMI_DISPATCH_PROTOCOL *This, + IN PCH_ESPI_SMI_DISPATCH_CALLBACK DispatchFunction, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock ev= ent has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + // + // If ESPI_SMI_LOCK is set, the register is blocked. + // + if (PmcIsEspiSmiLockSet ()) { + return EFI_ACCESS_DENIED; + } + + // + // @note: This service doesn't utilize the data base of mEspiSmiInstance= . + // While SMI is triggered it directly goes to the registing Dispa= tchFunction + // instead of EspiSmiCallback. + // + Status =3D PchSmiRecordInsert ( + &mSrcDescEspiSlave, + (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction, + PchEspiSmiEspiSlaveType, + DispatchHandle + ); + PchSmmClearSource (&mSrcDescEspiSlave); + PchSmmEnableSource (&mSrcDescEspiSlave); + + // + // Lock down the ESPI_SMI_LOCK after ESPI SMI is enabled. + // + PmcLockEspiSmi (); + // + // Keep the DispatchHandle which will be used for unregister function. + // + mEspiSmiInstance.PchSmiEspiHandle[EspiPmc] =3D *DispatchHandle; + + if (!EFI_ERROR (Status)) { + SmiHandlerProfileRegisterHandler (&gPchEspiSmiDispatchProtocolGuid, (E= FI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), NU= LL, 0); + } + + return Status; +} + +/** + eSPI SMI Dispatch Protocol instance to unregister a callback based on ha= ndle + + @param[in] This Not used + @param[in] DispatchHandle Handle acquired during registration + + @retval EFI_SUCCESS Unregister successful + @retval EFI_INVALID_PARAMETER DispatchHandle is null + @retval EFI_INVALID_PARAMETER DispatchHandle's forward link has ba= d pointer + @retval EFI_INVALID_PARAMETER DispatchHandle does not exist in dat= abase + @retval EFI_ACCESS_DENIED Unregistration is done after end of = DXE + @retval EFI_ACCESS_DENIED DispatchHandle is not allowed to unr= egistered +**/ +EFI_STATUS +EFIAPI +EspiSmiUnRegister ( + IN PCH_ESPI_SMI_DISPATCH_PROTOCOL *This, + IN EFI_HANDLE DispatchHandle + ) +{ + EFI_STATUS Status; + ESPI_TOP_LEVEL_TYPE EspiTopLevelType; + ESPI_SMI_TYPE EspiSmiType; + BOOLEAN SafeToDisable; + LIST_ENTRY *LinkInDb; + ESPI_SMI_RECORD *RecordPointer; + DATABASE_RECORD *RecordToDelete; + + if (DispatchHandle =3D=3D NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "UnRegister is not allowed if the SmmReadyToLock = event has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + if (((LIST_ENTRY *) DispatchHandle)->ForwardLink =3D=3D (LIST_ENTRY *) E= FI_BAD_POINTER) { + return EFI_INVALID_PARAMETER; + } + + // + // For DispatchHandle belongs to Espi Slave SMI, refuses the request of = unregistration. + // + if (mEspiSmiInstance.PchSmiEspiHandle[EspiPmc] =3D=3D DispatchHandle) { + DEBUG ((DEBUG_ERROR, "UnRegister is not allowed for ESPI Slave SMI han= dle! \n")); + return EFI_ACCESS_DENIED; + } + + // + // Iterate through all the database to find the record + // + for (EspiSmiType =3D 0; EspiSmiType < EspiSmiTypeMax; ++EspiSmiType) { + LinkInDb =3D GetFirstNode (&mEspiSmiInstance.CallbackDataBase[EspiSmiT= ype]); + + while (!IsNull (&mEspiSmiInstance.CallbackDataBase[EspiSmiType], LinkI= nDb)) { + if (LinkInDb !=3D (LIST_ENTRY *) DispatchHandle) { + LinkInDb =3D GetNextNode (&mEspiSmiInstance.CallbackDataBase[EspiS= miType], LinkInDb); + + } else { + // + // Found the source to be from this list + // + RemoveEntryList (LinkInDb); + RecordPointer =3D (ESPI_RECORD_FROM_LINK (LinkInDb)); + + if (mEspiSmiInstance.EspiSmiEventCounter[EspiSmiType] !=3D 0) { + if (--mEspiSmiInstance.EspiSmiEventCounter[EspiSmiType] =3D=3D 0= ) { + EspiSmiDisableSource (EspiSmiType); + } + } + + Status =3D gSmst->SmmFreePool (RecordPointer); + if (EFI_ERROR (Status)) { + ASSERT (FALSE); + } + + goto EspiSmiUnRegisterEnd; + } + } + } + // + // If the code reach here, the handle passed in cannot be found + // + DEBUG ((DEBUG_ERROR, "eSPI SMI handle is not in record database \n")); + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + +EspiSmiUnRegisterEnd: + + // + // Unregister and clear the handle from PchSmiDispatch + // + for (EspiTopLevelType =3D 0; EspiTopLevelType < EspiTopLevelTypeMax; ++E= spiTopLevelType) { + SafeToDisable =3D TRUE; + // + // Checks all the child events that belongs to a top level status in P= MC + // + for (EspiSmiType =3D mEspiSmiInstance.Barrier[EspiTopLevelType].Start;= EspiSmiType <=3D mEspiSmiInstance.Barrier[EspiTopLevelType].End; ++EspiSmi= Type) { + if (mEspiSmiInstance.EspiSmiEventCounter[EspiSmiType] !=3D 0) { + SafeToDisable =3D FALSE; + } + } + // + // Finally, disable the top level event in PMC + // + if (SafeToDisable) { + if (mEspiSmiInstance.PchSmiEspiHandle[EspiTopLevelType] !=3D NULL) { + Status =3D PchSmmCoreUnRegister (NULL, mEspiSmiInstance.PchSmiEspi= Handle[EspiTopLevelType]); + ASSERT_EFI_ERROR (Status); + mEspiSmiInstance.PchSmiEspiHandle[EspiTopLevelType] =3D NULL; + } + } + } + RecordToDelete =3D DATABASE_RECORD_FROM_LINK (DispatchHandle); + if (!EFI_ERROR (Status)) { + SmiHandlerProfileUnregisterHandler (&gPchEspiSmiDispatchProtocolGuid, = RecordToDelete->Callback, NULL, 0); + } + return EFI_SUCCESS; +} + +/** + Returns AND maks for clearing eSPI channel registers errors statuses + In addition to common status bit we add channel specific erro bits to av= oid clearing them + + @param[in] ChannelNumber Channel number (0 for PC, 1 for VW, = 2 for OOB, 3 for FA) + + @retval UINT32 AND mask with all the status bit mas= ked to not clear them by mistake +**/ +UINT32 +GetEspiChannelStatusClearMask ( + UINT8 ChannelNumber + ) +{ + UINT32 Data32; + + // Common error status bits for all channel registers + Data32 =3D B_ESPI_PCR_XERR_XNFES | B_ESPI_PCR_XERR_XFES; + + // Check channels for channel specific status bits + switch(ChannelNumber) { + case 0: // Peripheral Channel specific status bits + Data32 |=3D B_ESPI_PCR_PCERR_PCURD; + break; + case 3: // Flash Access Channel specific status bits + Data32 |=3D B_ESPI_PCR_FCERR_SAFBLK; + break; + default: + break; + } + + // Return the expected AND mask + return (UINT32)~(Data32); +} + +/** + Checks if channel error register data has Fatal Error bit set + If yes then reset the channel on slave + + @param[in] ChannelBaseAddress Base address + @param[in] ChannelNumber Channel number (0 for PC, 1 for VW, = 2 for OOB, 3 for FA) + @param[in] SlaveId Slave ID of which channel is to be r= eset +**/ +VOID +CheckSlaveChannelErrorAndReset ( + UINT16 ChannelBaseAddress, + UINT8 ChannelNumber, + UINT8 SlaveId + ) +{ + UINT32 Data32; + UINT16 ChannelAddress; + EFI_STATUS Status; + + if (ChannelNumber =3D=3D 2) { + DEBUG ((DEBUG_INFO, "Channel %d is not supported by this function due = to lack of error register\n", ChannelNumber)); + return; + } + + if (!IsEspiSlaveChannelSupported (SlaveId, ChannelNumber)) { + DEBUG ((DEBUG_WARN, "Channel %d is not supported by slave device\n", C= hannelNumber)); + return; + } + + // Calculate channel address based on slave id + ChannelAddress =3D (UINT16) (ChannelBaseAddress + (SlaveId * S_ESPI_PCR_= XERR)); + + // Reading channel error register data + Data32 =3D PchPcrRead32 (PID_ESPISPI, ChannelAddress); + + DEBUG ((DEBUG_INFO, "eSPI channel error register (0x%4X) has value 0x%8X= \n", ChannelAddress, Data32)); + + // Check Fatal Error status bit in channel error register data + if ((Data32 & B_ESPI_PCR_XERR_XFES) !=3D 0) { + Status =3D PchEspiSlaveChannelReset (SlaveId, ChannelNumber); + + if (EFI_ERROR (Status)) { + switch (Status) { + case EFI_UNSUPPORTED: + DEBUG ((DEBUG_ERROR, "Slave doesn't support channel %d\n", Chann= elNumber)); + break; + case EFI_TIMEOUT: + DEBUG ((DEBUG_ERROR, "Timeout occured during channel %d reset on= slave %d\n", ChannelNumber, SlaveId)); + break; + default: + DEBUG ((DEBUG_ERROR, "Error occured during channel %d reset\n", = ChannelNumber)); + break; + } + } else { + DEBUG ((DEBUG_INFO, "eSPI Slave %d channel %d reset ended successful= ly\n", SlaveId, ChannelNumber)); + // If channel reset was successfull clear the fatal error flag by wr= iting one + // we should be aware not to clear other status bits by mistake and = mask them + PchPcrAndThenOr32 ( + PID_ESPISPI, + ChannelAddress, + GetEspiChannelStatusClearMask (ChannelNumber), + B_ESPI_PCR_XERR_XFES + ); + } + } +} + +/** + eSPI SMI handler for Fatal Error recovery flow +**/ +VOID +EspiDefaultFatalErrorHandler ( + VOID + ) +{ + UINT32 Data32; + UINT8 SlaveId; + UINT8 MaxSlavesCount; + + DEBUG ((DEBUG_INFO, "[EspiRecoverFromFatalError] Enter\n")); + + MaxSlavesCount =3D IsEspiSecondSlaveSupported () ? 2 : 1; + + DEBUG ((DEBUG_INFO, "[EspiRecoverFromFatalError] MaxSlavesCount %d\n", M= axSlavesCount)); + + for (SlaveId =3D 0; SlaveId < MaxSlavesCount; ++SlaveId) { + // + // Check if slave has SLCRR bit set. If it does it means it needs reco= very. + // + Data32 =3D PchPcrRead32 (PID_ESPISPI, (UINT16) (R_ESPI_PCR_LNKERR_SLV0= + (SlaveId * S_ESPI_PCR_XERR))); + + DEBUG ((DEBUG_INFO, "[EspiRecoverFromFatalError] Slave %d LNKERR reg 0= x%8X\n", SlaveId, Data32)); + // + // If SLCRR[31] bit is set we need to recover that slave + // + if ((Data32 & B_ESPI_PCR_LNKERR_SLV0_SLCRR) !=3D 0) { + // 1. Perform in-band reset + PchEspiSlaveInBandReset (SlaveId); + + // 2. Channels reset + CheckSlaveChannelErrorAndReset (R_ESPI_PCR_PCERR_SLV0, 0, SlaveId); = // Peripheral channel reset + CheckSlaveChannelErrorAndReset (R_ESPI_PCR_VWERR_SLV0, 1, SlaveId); = // Virtual Wire channel reset + + // Flash Access channel is not supported for CS1# + if (SlaveId =3D=3D 0) { + CheckSlaveChannelErrorAndReset (R_ESPI_PCR_PCERR_SLV0, 3, SlaveId)= ; // Flash Access channel reset + } + + // Clear SLCRR bit of slave after all channels recovery was done + PchPcrAndThenOr32 ( + PID_ESPISPI, + (UINT16) (R_ESPI_PCR_LNKERR_SLV0 + (SlaveId * S_ESPI_PCR_XERR)), + (UINT32)~(B_ESPI_PCR_LNKERR_SLV0_LFET1S), + (UINT32) (B_ESPI_PCR_LNKERR_SLV0_SLCRR) + ); + } + } + + DEBUG ((DEBUG_INFO, "[EspiRecoverFromFatalError] Exit\n")); +} + + diff --git a/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/Pc= hSmmGpi.c b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/Pch= SmmGpi.c new file mode 100644 index 0000000000..43277f0938 --- /dev/null +++ b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmGpi= .c @@ -0,0 +1,254 @@ +/** @file + File to contain all the hardware specific stuff for the Smm Gpi dispatch= protocol. + + Copyright (c) 2019 Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include "PchSmm.h" +#include "PchSmmHelpers.h" +#include +#include +#include + +// +// Structure for GPI SMI is a template which needs to have +// GPI Smi bit offset and Smi Status & Enable registers updated (according= ly +// to choosen group and pad number) after adding it to SMM Callback databa= se +// + +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mPchGpiSourceDescT= emplate =3D { + 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_PROT= OCOL 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 contex= t. + The caller fills this context in before c= alling + the register function to indicate to the = register + function the GPI(s) for which the dispatc= h function + should be invoked. + @param[out] DispatchHandle Handle generated by the dispatcher to tra= ck the + function instance. + + @retval EFI_SUCCESS The dispatch function has been successful= ly + registered and the SMI source has been en= abled. + @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 CONST 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 Data32Or; + UINT32 Data32And; + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the EndOfDxe event ha= s been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + Status =3D 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 =3D DispatchFunction; + Record.ChildContext.Gpi =3D *RegisterContext; + Record.ProtocolType =3D GpiType; + Record.Signature =3D DATABASE_RECORD_SIGNATURE; + + CopyMem (&Record.SrcDesc, &mPchGpiSourceDescTemplate, sizeof (PCH_SMM_SO= URCE_DESC) ); + + Record.SrcDesc.Sts[0].Reg.Data.raw =3D GpiSmiStsRegAddress; // GPI SMI = Status register + Record.SrcDesc.Sts[0].Bit =3D GpiSmiBitOffset; // Bit posi= tion for selected pad + + // + // Insert GpiSmi handler to PchSmmCore database + // + *DispatchHandle =3D NULL; + + Status =3D SmmCoreInsertRecord ( + &Record, + DispatchHandle + ); + ASSERT_EFI_ERROR (Status); + + SmiHandlerProfileRegisterHandler (&gEfiSmmGpiDispatch2ProtocolGuid, (EFI= _SMM_HANDLER_ENTRY_POINT2) DispatchFunction, (UINTN)RETURN_ADDRESS (0), (vo= id *)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 =3D (UINT32)~(1u << GpiSmiBitOffset); + MmioAnd32 (GpiHostSwOwnRegAddress, Data32And); + + // + // Add HOSTSW_OWN programming into S3 boot script + // + Data32Or =3D 0; + S3BootScriptSaveMemReadWrite (S3BootScriptWidthUint32, GpiHostSwOwnRegAd= dress, &Data32Or, &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_PRO= TOCOL instance. + @param[in] DispatchHandle Handle of dispatch function to deregiste= r. + + @retval EFI_SUCCESS The dispatch function has been successfu= lly + unregistered and the SMI source has been= disabled + if there are no other registered child d= ispatch + 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 =3D=3D NULL) { + return EFI_INVALID_PARAMETER; + } + + RecordToDelete =3D DATABASE_RECORD_FROM_LINK (DispatchHandle); + if ((RecordToDelete->Signature !=3D DATABASE_RECORD_SIGNATURE) || + (RecordToDelete->ProtocolType !=3D GpiType)) { + return EFI_INVALID_PARAMETER; + } + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "UnRegister is not allowed if the SmmReadyToLock = event has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + DisableGpiSmiSource =3D TRUE; + // + // Loop through all sources in record linked list to see if any other GP= I SMI + // is installed on the same pin. If no then disable GPI SMI capability o= n this pad + // + LinkInDb =3D GetFirstNode (&mPrivateData.CallbackDataBase); + while (!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) { + RecordInDb =3D DATABASE_RECORD_FROM_LINK (LinkInDb); + LinkInDb =3D GetNextNode (&mPrivateData.CallbackDataBase, &RecordInDb-= >Link); + // + // If this is the record to delete skip it + // + if (RecordInDb =3D=3D RecordToDelete) { + continue; + } + // + // Check if record is GPI SMI type + // + if (RecordInDb->ProtocolType =3D=3D GpiType) { + // + // Check if same GPIO pad is the source of this SMI + // + if (RecordInDb->ChildContext.Gpi.GpiNum =3D=3D RecordToDelete->Child= Context.Gpi.GpiNum) { + DisableGpiSmiSource =3D FALSE; + break; + } + } + } + + if (DisableGpiSmiSource) { + GpioGetPadAndSmiRegs ( + (UINT32) RecordToDelete->ChildContext.Gpi.GpiNum, + &GpioPad, + &GpiSmiBitOffset, + &GpiHostSwOwnRegAddress, + &GpiSmiStsRegAddress + ); + + Data32Or =3D 1u << GpiSmiBitOffset; + Data32And =3D 0xFFFFFFFF; + MmioOr32 (GpiHostSwOwnRegAddress, Data32Or); + S3BootScriptSaveMemReadWrite (S3BootScriptWidthUint32, GpiHostSwOwnReg= Address, &Data32Or, &Data32And); + } + + + RemoveEntryList (&RecordToDelete->Link); + ZeroMem (RecordToDelete, sizeof (DATABASE_RECORD)); + Status =3D gSmst->SmmFreePool (RecordToDelete); + + if (EFI_ERROR (Status)) { + ASSERT (FALSE); + return Status; + } + SmiHandlerProfileUnregisterHandler (&gEfiSmmGpiDispatch2ProtocolGuid, Re= cordToDelete->Callback, NULL, 0); + return EFI_SUCCESS; +} diff --git a/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/Pc= hSmmHelpers.c b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm= /PchSmmHelpers.c new file mode 100644 index 0000000000..f6413921eb --- /dev/null +++ b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmHel= pers.c @@ -0,0 +1,358 @@ +/** @file + Helper functions for PCH SMM dispatcher. + + Copyright (c) 2019 Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include "PchSmmHelpers.h" +#include +#include +#include +#include + +/// +/// #define BIT_ZERO 0x00000001 +/// +GLOBAL_REMOVE_IF_UNREFERENCED CONST UINT32 BIT_ZERO =3D 0x00000001; + +/// +/// SUPPORT / HELPER FUNCTIONS (PCH version-independent) +/// + +/** + Compare 2 SMM source descriptors' enable settings. + + @param[in] Src1 Pointer to the PCH SMI source descriptio= n table 1 + @param[in] Src2 Pointer to the PCH SMI source descriptio= n 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 =3D TRUE; + for (DescIndex =3D 0; DescIndex < NUM_EN_BITS; DescIndex++) { + /// + /// It's okay to compare a NULL bit description to a non-NULL bit desc= ription. + /// They are unequal and these tests will generate the correct result. + /// + if (Src1->En[DescIndex].Bit !=3D Src2->En[DescIndex].Bit || + Src1->En[DescIndex].Reg.Type !=3D Src2->En[DescIndex].Reg.Type || + Src1->En[DescIndex].Reg.Data.raw !=3D Src2->En[DescIndex].Reg.Data= .raw + ) { + IsEqual =3D FALSE; + break; + /// + /// out of for loop + /// + } + } + + return IsEqual; +} + +/** + Compare a bit descriptor to the enables of source descriptor. Includes n= ull address type. + + @param[in] BitDesc Pointer to the PCH SMI bit descriptor + @param[in] Src Pointer to the PCH SMI source descriptio= n table 2 + + @retval TRUE The bit desc is equal to any of the enab= les 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 =3D FALSE; + + for (DescIndex =3D 0; DescIndex < NUM_EN_BITS; ++DescIndex) { + if ((BitDesc->Reg.Type =3D=3D Src->En[DescIndex].Reg.Type) && + (BitDesc->Reg.Data.raw =3D=3D Src->En[DescIndex].Reg.Data.raw) && + (BitDesc->Bit =3D=3D Src->En[DescIndex].Bit)) { + IsEqual =3D TRUE; + break; + } + } + return IsEqual; +} + +/** + Compare 2 SMM source descriptors' statuses. + + @param[in] Src1 Pointer to the PCH SMI source descriptio= n table 1 + @param[in] Src2 Pointer to the PCH SMI source descriptio= n table 2 + + @retval TRUE The statuses of the 2 SMM source descrip= tors are identical. + @retval FALSE The statuses of the 2 SMM source descrip= tors are not identical. +**/ +BOOLEAN +CompareStatuses ( + CONST IN PCH_SMM_SOURCE_DESC *Src1, + CONST IN PCH_SMM_SOURCE_DESC *Src2 + ) +{ + BOOLEAN IsEqual; + UINTN DescIndex; + + IsEqual =3D TRUE; + + for (DescIndex =3D 0; DescIndex < NUM_STS_BITS; DescIndex++) { + /// + /// It's okay to compare a NULL bit description to a non-NULL bit desc= ription. + /// They are unequal and these tests will generate the correct result. + /// + if (Src1->Sts[DescIndex].Bit !=3D Src2->Sts[DescIndex].Bit || + Src1->Sts[DescIndex].Reg.Type !=3D Src2->Sts[DescIndex].Reg.Type |= | + Src1->Sts[DescIndex].Reg.Data.raw !=3D Src2->Sts[DescIndex].Reg.Da= ta.raw + ) { + IsEqual =3D FALSE; + break; + /// + /// out of for loop + /// + } + } + + return IsEqual; +} + +/** + Compare 2 SMM source descriptors, based on Enable settings and Status se= ttings of them. + + @param[in] Src1 Pointer to the PCH SMI source descriptio= n table 1 + @param[in] Src2 Pointer to the PCH SMI source descriptio= n table 2 + + @retval TRUE The 2 SMM source descriptors are identic= al. + @retval FALSE The 2 SMM source descriptors are not ide= ntical. +**/ +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 descriptio= n 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 =3D=3D 1. An ACPI OS i= s present, + /// so we shouldn't do anything w/ this source until SciEn =3D=3D 0. + /// + if ((Src->Flags =3D=3D PCH_SMM_SCI_EN_DEPENDENT) && (SciEn)) { + return FALSE; + } + + /// + /// Checking top level SMI status. If the status is not active, return f= alse immediately + /// + if (!IS_BIT_DESC_NULL (Src->PmcSmiSts)) { + if ((Src->PmcSmiSts.Reg.Type =3D=3D ACPI_ADDR_TYPE) && + (Src->PmcSmiSts.Reg.Data.acpi =3D=3D R_ACPI_IO_SMI_STS) && + ((SmiStsValue & (1u << Src->PmcSmiSts.Bit)) =3D=3D 0)) { + return FALSE; + } + } + + // + // Special handling for NMI bit since it requires PMC IPC command. + // Do w/a here instead of in ReadBitDesc to reduce the PMC IPC command u= sage. + // + // The PCR[ITSS].NMI register can only be accessed with BOOT_SAI and SMM= _SAI. + // Since in CFL there is no SMM_SAI it needs PMC assistance to access th= is register. + // + if ((Src->En[0].Reg.Data.Pcr.Fields.Pid =3D=3D PID_ITSS) && + (Src->En[0].Reg.Data.Pcr.Fields.Offset =3D=3D R_ITSS_PCR_NMI)) + { + UINT32 ItssNmi; + ItssNmi =3D PmcGetNmiControl (); + if ((ItssNmi & (BIT0 << Src->En[0].Bit)) && + (ItssNmi & (BIT0 << Src->Sts[0].Bit))) + { + return TRUE; + } else { + return FALSE; + } + } + + /// + /// Read each bit desc from hardware and make sure it's a one + /// + for (DescIndex =3D 0; DescIndex < NUM_EN_BITS; DescIndex++) { + if (!IS_BIT_DESC_NULL (Src->En[DescIndex])) { + if ((Src->En[DescIndex].Reg.Type =3D=3D ACPI_ADDR_TYPE) && + (Src->En[DescIndex].Reg.Data.acpi =3D=3D R_ACPI_IO_SMI_EN) && + ((SmiEnValue & (1u << Src->En[DescIndex].Bit)) =3D=3D 0)) { + return FALSE; + } else if (ReadBitDesc (&Src->En[DescIndex]) =3D=3D 0) { + return FALSE; + } + } + } + + /// + /// Read each bit desc from hardware and make sure it's a one + /// + for (DescIndex =3D 0; DescIndex < NUM_STS_BITS; DescIndex++) { + if (!IS_BIT_DESC_NULL (Src->Sts[DescIndex])) { + if ((Src->Sts[DescIndex].Reg.Type =3D=3D ACPI_ADDR_TYPE) && + (Src->Sts[DescIndex].Reg.Data.acpi =3D=3D R_ACPI_IO_SMI_STS) && + ((SmiStsValue & (1u << Src->Sts[DescIndex].Bit)) =3D=3D 0)) { + return FALSE; + } else if (ReadBitDesc (&Src->Sts[DescIndex]) =3D=3D 0) { + return FALSE; + } + } + } + + return TRUE; +} + +/** + Enable the SMI source event by set the SMI enable bit, this function wou= ld also clear SMI + status bit to make initial state is correct + + @param[in] SrcDesc Pointer to the PCH SMI source descriptio= n table + +**/ +VOID +PchSmmEnableSource ( + CONST PCH_SMM_SOURCE_DESC *SrcDesc + ) +{ + UINTN DescIndex; + + /// + /// Set enables to 1 by writing a 1 + /// + for (DescIndex =3D 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 =3D 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 descriptio= n table + +**/ +VOID +PchSmmDisableSource ( + CONST PCH_SMM_SOURCE_DESC *SrcDesc + ) +{ + UINTN DescIndex; + + for (DescIndex =3D 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 descriptio= n table + +**/ +VOID +PchSmmClearSource ( + CONST PCH_SMM_SOURCE_DESC *SrcDesc + ) +{ + UINTN DescIndex; + + for (DescIndex =3D 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 descriptio= n table + +**/ +VOID +PchSmmClearSourceAndBlock ( + CONST PCH_SMM_SOURCE_DESC *SrcDesc + ) +{ + UINTN DescIndex; + BOOLEAN IsSet; + + for (DescIndex =3D 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 =3D TRUE; + while (IsSet) { + IsSet =3D ReadBitDesc (&SrcDesc->Sts[DescIndex]); + /// + /// IsSet will eventually clear -- or else we'll have + /// an infinite loop. + /// + } + } + } +} + diff --git a/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/Pc= hSmmPeriodicTimer.c b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatch= er/Smm/PchSmmPeriodicTimer.c new file mode 100644 index 0000000000..9a5ae464e0 --- /dev/null +++ b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmPer= iodicTimer.c @@ -0,0 +1,675 @@ +/** @file + File to contain all the hardware specific stuff for the Periodical Timer= dispatch protocol. + + Copyright (c) 2019 Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include "PchSmmHelpers.h" +#include +#include + +// +// There is only one instance for PeriodicTimerCommBuffer. +// It's safe in SMM since there is no re-entry for the function. +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_SMM_PERIODIC_TIMER_CONTEXT mPch= PeriodicTimerCommBuffer; + +typedef enum { + PERIODIC_TIMER=3D 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 =3D 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] =3D { + { + 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[N= UM_TIMERS] =3D { + { + 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 insta= nce. +**/ +VOID +PchSmmPeriodicTimerProgramTimers ( + IN CONST PCH_SMM_SOURCE_DESC *SrcDesc + ); + +/** + Convert the dispatch context to the timer interval, this function will a= ssert 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 con= text +**/ +TIMER_INTERVAL * +ContextToTimerInterval ( + IN PCH_SMM_CONTEXT *DispatchContext + ) +{ + UINTN loopvar; + + /// + /// Determine which timer this child is using + /// + for (loopvar =3D 0; loopvar < NUM_INTERVALS; loopvar++) { + if (((DispatchContext->PeriodicTimer.SmiTickInterval =3D=3D 0) && + (DispatchContext->PeriodicTimer.Period >=3D mSmmPeriodicTimerInte= rvals[loopvar].Interval)) || + (DispatchContext->PeriodicTimer.SmiTickInterval =3D=3D mSmmPeriodi= cTimerIntervals[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 inst= ances + @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 =3D ContextToTimerInterval (DispatchContext); + if (TimerInterval =3D=3D 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 =3D=3D PeriodicTimerType); + + TimerInterval =3D ContextToTimerInterval (&Record->ChildContext); + if (TimerInterval =3D=3D 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 +=3D mTimers[TimerInterval->AssociatedTimer= ].MinReqInterval; + *HwContext =3D 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 =3D DATABASE_RECORD_FROM_CHILDCONTEXT (ChildContext); + + if (Record->MiscData.ElapsedTime >=3D ChildContext->PeriodicTimer.Period= ) { + /// + /// For EDKII, the ElapsedTime is reset when PeriodicTimerGetCommBuffe= r + /// + 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 structur= e + +**/ +VOID +EFIAPI +PeriodicTimerGetCommBuffer ( + IN DATABASE_RECORD *Record, + OUT VOID **CommBuffer, + OUT UINTN *CommBufferSize + ) +{ + ASSERT (Record->ProtocolType =3D=3D PeriodicTimerType); + + mPchPeriodicTimerCommBuffer.ElapsedTime =3D Record->MiscData.ElapsedTime= ; + + /// + /// For EDKII, the ElapsedTime is reset here + /// + Record->MiscData.ElapsedTime =3D 0; + + /// + /// Return the CommBuffer + /// + *CommBuffer =3D (VOID *) &mPchPeriodicTimerCommBuffer; + *CommBufferSize =3D sizeof (EFI_SMM_PERIODIC_TIMER_CONTEXT); +} + +/** + Program Smm Periodic Timer + + @param[in] SrcDesc Pointer to the PCH_SMM_SOURCE_DESC insta= nce. +**/ +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 =3D 0; Timer < NUM_TIMERS; Timer++) { + mTimers[Timer].MinReqInterval =3D ~ (UINT64) 0x0; + mTimers[Timer].NumChildren =3D 0; + } + + LinkInDb =3D GetFirstNode (&mPrivateData.CallbackDataBase); + while (!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) { + RecordInDb =3D DATABASE_RECORD_FROM_LINK (LinkInDb); + if (RecordInDb->ProtocolType =3D=3D PeriodicTimerType) { + /// + /// This child is registerd with the PeriodicTimer protocol + /// + TimerInterval =3D ContextToTimerInterval (&RecordInDb->ChildContext)= ; + if (TimerInterval =3D=3D NULL) { + return; + } + + Timer =3D TimerInterval->AssociatedTimer; + if (Timer < 0 || Timer >=3D NUM_TIMERS) { + ASSERT (FALSE); + CpuDeadLoop (); + return; + } + + if (mTimers[Timer].MinReqInterval > RecordInDb->ChildContext.Periodi= cTimer.SmiTickInterval) { + mTimers[Timer].MinReqInterval =3D RecordInDb->ChildContext.Periodi= cTimer.SmiTickInterval; + } + + mTimers[Timer].NumChildren++; + } + + LinkInDb =3D 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 =3D INDEX_TIME_64s; + break; + + case TIME_32s: + PmcSetPeriodicSmiRate (PmcPeriodicSmiRate32s); + mTimers[PERIODIC_TIMER].CurrentSetting =3D INDEX_TIME_32s; + break; + + case TIME_16s: + PmcSetPeriodicSmiRate (PmcPeriodicSmiRate16s); + mTimers[PERIODIC_TIMER].CurrentSetting =3D INDEX_TIME_16s; + break; + + case TIME_8s: + PmcSetPeriodicSmiRate (PmcPeriodicSmiRate8s); + mTimers[PERIODIC_TIMER].CurrentSetting =3D INDEX_TIME_8s; + break; + + default: + ASSERT (FALSE); + break; + } + + /// + /// Restart the timer here, just need to clear the SMI + /// + if (SrcDesc->Sts[0].Bit =3D=3D 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 =3D INDEX_TIME_64ms; + break; + + case TIME_32ms: + PmcSetSwSmiRate (PmcSwSmiRate32ms); + mTimers[SWSMI_TIMER].CurrentSetting =3D INDEX_TIME_32ms; + break; + + case TIME_16ms: + PmcSetSwSmiRate (PmcSwSmiRate16ms); + mTimers[SWSMI_TIMER].CurrentSetting =3D INDEX_TIME_16ms; + break; + + case TIME_1_5ms: + PmcSetSwSmiRate (PmcSwSmiRate1p5ms); + mTimers[SWSMI_TIMER].CurrentSetting =3D INDEX_TIME_1_5ms; + break; + + default: + ASSERT (FALSE); + break; + } + + /// + /// Restart the timer here, need to disable, clear, then enable to res= tart this timer + /// + if (SrcDesc->Sts[0].Bit =3D=3D 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_DI= SPATCH2_PROTOCOL instance. + @param[in, out] SmiTickInterval Pointer to pointer of the next shorter S= MI 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; + + if (SmiTickInterval =3D=3D NULL) { + ASSERT(FALSE); + return EFI_INVALID_PARAMETER; + } + + IntervalPointer =3D (TIMER_INTERVAL *) *SmiTickInterval; + + if (IntervalPointer =3D=3D NULL) { + /// + /// The first time child requesting an interval + /// + IntervalPointer =3D &mSmmPeriodicTimerIntervals[0]; + } else if (IntervalPointer =3D=3D &mSmmPeriodicTimerIntervals[NUM_INTERV= ALS - 1]) { + /// + /// At end of the list + /// + IntervalPointer =3D NULL; + } else { + if ((IntervalPointer >=3D &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 !=3D NULL) { + *SmiTickInterval =3D &IntervalPointer->Interval; + } else { + *SmiTickInterval =3D NULL; + } + + return EFI_SUCCESS; +} + +/** + This function is responsible for calculating and enabling any timers tha= t are required + to dispatch messages to children. The SrcDesc argument isn't acutally us= ed. + + @param[in] SrcDesc Pointer to the PCH_SMM_SOURCE_DESC insta= nce. + +**/ +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 PeriodicT= imer. + @retval FALSE The handle is not in type of Perio= dicTimer. +**/ +BOOLEAN +IsSmmPeriodicTimerHandle ( + IN EFI_HANDLE DispatchHandle + ) +{ + DATABASE_RECORD *RecordInDb; + LIST_ENTRY *LinkInDb; + + LinkInDb =3D GetFirstNode (&mPrivateData.CallbackDataBase); + while (!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) { + if (DispatchHandle =3D=3D (EFI_HANDLE) LinkInDb) { + RecordInDb =3D DATABASE_RECORD_FROM_LINK (LinkInDb); + if (RecordInDb->ProtocolType =3D=3D PeriodicTimerType) { + return TRUE; + } + } + LinkInDb =3D GetNextNode (&mPrivateData.CallbackDataBase, LinkInDb); + } + return FALSE; +} + +/** + Pause SMM periodic timer callback function. + + This function disable the SMI enable of SMI timer according to the Dispa= tchHandle, + 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; + TIMER_INTERVAL *TimerInterval; + + if (IsSmmPeriodicTimerHandle (DispatchHandle) =3D=3D FALSE) { + return EFI_INVALID_PARAMETER; + } + + RecordInDb =3D DATABASE_RECORD_FROM_LINK (DispatchHandle); + TimerInterval =3D NULL; + TimerInterval =3D ContextToTimerInterval (&RecordInDb->ChildContext); + if (TimerInterval =3D=3D NULL) { + return EFI_INVALID_PARAMETER; + } + PchSmmDisableSource (&mTimerSourceDesc[TimerInterval->AssociatedTimer]); + return EFI_SUCCESS; +} + +/** + Resume SMM periodic timer callback function. + + This function enable the SMI enable of SMI timer according to the Dispat= chHandle, + 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; + TIMER_INTERVAL *TimerInterval; + + if (IsSmmPeriodicTimerHandle (DispatchHandle) =3D=3D FALSE) { + return EFI_INVALID_PARAMETER; + } + + RecordInDb =3D DATABASE_RECORD_FROM_LINK (DispatchHandle); + TimerInterval =3D NULL; + TimerInterval =3D ContextToTimerInterval (&RecordInDb->ChildContext); + if (TimerInterval =3D=3D NULL) { + return EFI_INVALID_PARAMETER; + } + PchSmmEnableSource (&mTimerSourceDesc[TimerInterval->AssociatedTimer]); + return EFI_SUCCESS; +} + +GLOBAL_REMOVE_IF_UNREFERENCED PCH_SMM_PERIODIC_TIMER_CONTROL_PROTOCOL mPch= SmmPeriodicTimerControlProtocol =3D { + PchSmmPeriodicTimerControlPause, + PchSmmPeriodicTimerControlResume +}; + +/** + Install PCH SMM periodic timer control protocol + + @param[in] Handle handle for this driver + + @retval EFI_SUCCESS Driver initialization completed su= ccessfully +**/ +EFI_STATUS +EFIAPI +InstallPchSmmPeriodicTimerControlProtocol ( + IN EFI_HANDLE Handle + ) +{ + EFI_STATUS Status; + + // + // Install protocol interface + // + Status =3D gSmst->SmmInstallProtocolInterface ( + &Handle, + &gPchSmmPeriodicTimerControlGuid, + EFI_NATIVE_INTERFACE, + &mPchSmmPeriodicTimerControlProtocol + ); + ASSERT_EFI_ERROR (Status); + + return EFI_SUCCESS; +} + diff --git a/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/Pc= hSmmPowerButton.c b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher= /Smm/PchSmmPowerButton.c new file mode 100644 index 0000000000..17898b899b --- /dev/null +++ b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmPow= erButton.c @@ -0,0 +1,83 @@ +/** @file + File to contain all the hardware specific stuff for the Smm Power Button= dispatch protocol. + + Copyright (c) 2019 Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include + +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mPowerButtonSource= Desc =3D { + 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 + ) +{ + if (PmcGetPwrBtnLevel ()) { + Context->PowerButton.Phase =3D EfiPowerButtonExit; + } else { + Context->PowerButton.Phase =3D EfiPowerButtonEntry; + } +} + +/** + Check whether Power Button status of two contexts match + + @param[in] Context1 Context 1 that includes Power Button sta= tus 1 + @param[in] Context2 Context 2 that includes Power Button sta= tus 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 (BOOLEAN) (Context1->PowerButton.Phase =3D=3D Context2->PowerButt= on.Phase); +} diff --git a/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/Pc= hSmmSw.c b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchS= mmSw.c new file mode 100644 index 0000000000..35ecfe5238 --- /dev/null +++ b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmSw.= c @@ -0,0 +1,385 @@ +/** @file + File to contain all the hardware specific stuff for the Smm Sw dispatch = protocol. + + Copyright (c) 2019 Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include "PchSmmHelpers.h" +#include +#include +#include + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_SMM_CPU_PROTOCOL *mSmmCpuProtoc= ol; + +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 =3D = { + 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 dat= abase + + @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 =3D GetFirstNode (&mSwSmiCallbackDataBase); + while (!IsNull (&mSwSmiCallbackDataBase, LinkInDb)) { + SwSmiRecord =3D SW_SMI_RECORD_FROM_LINK (LinkInDb); + if (SwSmiRecord->Context.SwSmiInputValue =3D=3D SwSmiInputValue) { + return EFI_INVALID_PARAMETER; + } + LinkInDb =3D 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 calle= d when the software + SMI source specified by RegisterContext->SwSmiCpuIndex is detected. On r= eturn, + DispatchHandle contains a unique handle which may be used later to unreg= ister the function + using UnRegister(). + + @param[in] This Pointer to the EFI_SMM_SW_DISPATCH2_PRO= TOCOL instance. + @param[in] DispatchFunction Function to register for handler when t= he specified software + SMI is generated. + @param[in, out] RegisterContext Pointer to the dispatch function's cont= ext. + The caller fills this context in before= calling + the register function to indicate to th= e register + function which Software SMI input value= the + dispatch function should be invoked for= . + @param[out] DispatchHandle Handle generated by the dispatcher to t= rack the + function instance. + + @retval EFI_SUCCESS The dispatch function has been successful= ly + registered and the SMI source has been en= abled. + @retval EFI_DEVICE_ERROR The SW driver was unable to enable the SM= I source. + @retval EFI_INVALID_PARAMETER RegisterContext is invalid. The SW SMI in= put 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 =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock ev= ent has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + // + // Find available SW SMI value if the input is -1 + // + if (DispatchContext->SwSmiInputValue =3D=3D (UINTN) -1) { + for (Index =3D 1; Index < MAXIMUM_SWI_VALUE; Index++) { + Status =3D SmiInputValueDuplicateCheck (Index); + if (!EFI_ERROR (Status)) { + DispatchContext->SwSmiInputValue =3D Index; + break; + } + } + if (DispatchContext->SwSmiInputValue =3D=3D (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 proto= col. + // + if (DispatchContext->SwSmiInputValue >=3D MAXIMUM_SWI_VALUE) { + return EFI_INVALID_PARAMETER; + } + + Status =3D SmiInputValueDuplicateCheck (DispatchContext->SwSmiInputValue= ); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + + // + // Create database record and add to database + // + Status =3D 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 =3D SW_SMI_RECORD_SIGNATURE; + SwSmiRecord->Context.SwSmiInputValue =3D DispatchContext->SwSmiInputValu= e; + SwSmiRecord->Callback =3D 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", SwSmiR= ecord->Context.SwSmiInputValue, SwSmiRecord)); + + InsertTailList (&mSwSmiCallbackDataBase, &SwSmiRecord->Link); + + // + // Child's handle will be the address linked list link in the record + // + *DispatchHandle =3D (EFI_HANDLE) (&SwSmiRecord->Link); + + return EFI_SUCCESS; +} + +/** + Unregister a child SMI source dispatch function for the specified softwa= re 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_PROTO= COL instance. + @param[in] DispatchHandle Handle of dispatch function to deregister= . + + @retval EFI_SUCCESS The dispatch function has been successful= ly 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 =3D=3D 0) { + return EFI_INVALID_PARAMETER; + } + + // + // Return access denied if the SmmReadyToLock event has been triggered + // + if (mReadyToLock =3D=3D TRUE) { + DEBUG ((DEBUG_ERROR, "UnRegister is not allowed if the SmmReadyToLock = event has been triggered! \n")); + return EFI_ACCESS_DENIED; + } + + RecordToDelete =3D SW_SMI_RECORD_FROM_LINK (DispatchHandle); + // + // Take the entry out of the linked list + // + if (RecordToDelete->Signature !=3D SW_SMI_RECORD_SIGNATURE) { + return EFI_INVALID_PARAMETER; + } + + RemoveEntryList (&RecordToDelete->Link); + ZeroMem (RecordToDelete, sizeof (SW_SMI_RECORD)); + Status =3D gSmst->SmmFreePool (RecordToDelete); + ASSERT_EFI_ERROR (Status); + + return EFI_SUCCESS; +} + +/** + Main entry point for an SMM handler dispatch or communicate-based callba= ck. + + @param[in] DispatchHandle The unique handle assigned to this handle= r by SmiHandlerRegister(). + @param[in] Context Points to an optional handler context whi= ch was specified when the + handler was registered. + @param[in,out] CommBuffer A pointer to a collection of data in memo= ry that will + be conveyed from a non-SMM environment in= to an SMM environment. + @param[in,out] CommBufferSize The size of the CommBuffer. + + @retval EFI_SUCCESS The interrupt was handled an= d quiesced. No other handlers + should still be called. + @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quies= ced but other handlers should + still be called. + @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pendi= ng and other handlers should still + be called. + @retval EFI_INTERRUPT_PENDING The interrupt could not be q= uiesced. +**/ +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 =3D sizeof (EFI_SMM_SW_CONTEXT); + // + // The value in DataPort might not be accurate in multiple thread enviro= nment. + // There might be racing condition for R_PCH_IO_APM_STS port. + // Therefor, this is just for reference. + // + SwSmiCommBuffer.DataPort =3D IoRead8 (R_PCH_IO_APM_STS); + + for (CpuIndex =3D 0; CpuIndex < gSmst->NumberOfCpus; CpuIndex++) { + Status =3D 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 !=3D R_PCH_IO_APM_CNT) || + (SmiIoInfo.IoType !=3D EFI_SMM_SAVE_STATE_IO_TYPE_OUTPUT) || + (SmiIoInfo.IoWidth !=3D EFI_SMM_SAVE_STATE_IO_WIDTH_UINT8)) + { + continue; + } + // + // If the IO data is used for SmmControl protocol, skip it. + // + if (SmiIoInfo.IoData =3D=3D 0xFF) { + continue; + } + + SwSmiCommBuffer.SwSmiCpuIndex =3D CpuIndex; + SwSmiCommBuffer.CommandPort =3D (UINT8) SmiIoInfo.IoData; + + LinkInDb =3D GetFirstNode (&mSwSmiCallbackDataBase); + while (!IsNull (&mSwSmiCallbackDataBase, LinkInDb)) { + SwSmiRecord =3D SW_SMI_RECORD_FROM_LINK (LinkInDb); + if (SwSmiRecord->Context.SwSmiInputValue =3D=3D SmiIoInfo.IoData) { + SwSmiRecord->Callback ((EFI_HANDLE) &SwSmiRecord->Link, &SwSmiReco= rd->Context, &SwSmiCommBuffer, &SwSmiCommBufferSize); + } + LinkInDb =3D GetNextNode (&mSwSmiCallbackDataBase, &SwSmiRecord->Lin= k); + } + } + + 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 =3D gSmst->SmmLocateProtocol (&gEfiSmmCpuProtocolGuid, NULL, (VOI= D **)&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 =3D DATABASE_RECORD_SIGNATURE; + Record.Callback =3D PchSwSmiDispatcher; + Record.ProtocolType =3D SwType; + + CopyMem (&Record.SrcDesc, &mSwSourceDesc, sizeof (PCH_SMM_SOURCE_DESC)); + + DispatchHandle =3D NULL; + Status =3D SmmCoreInsertRecord ( + &Record, + &DispatchHandle + ); + ASSERT_EFI_ERROR (Status); +} diff --git a/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/Pc= hSmmSx.c b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchS= mmSx.c new file mode 100644 index 0000000000..52bb906315 --- /dev/null +++ b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmSx.= c @@ -0,0 +1,229 @@ +/** @file + File to contain all the hardware specific stuff for the Smm Sx dispatch = protocol. + + Copyright (c) 2019 Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include "PchSmmHelpers.h" +#include +#include + +#define PROGRESS_CODE_S3_SUSPEND_END PcdGet32 (PcdProgressCodeS3SuspendEn= d) + +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSxSourceDesc =3D = { + 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 t= o be filled + +**/ +VOID +EFIAPI +SxGetContext ( + IN DATABASE_RECORD *Record, + OUT PCH_SMM_CONTEXT *Context + ) +{ + UINT32 Pm1Cnt; + + Pm1Cnt =3D IoRead32 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_PM1_CNT)); + + /// + /// By design, the context phase will always be ENTRY + /// + Context->Sx.Phase =3D 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 =3D SxS0; + break; + + case V_ACPI_IO_PM1_CNT_S1: + Context->Sx.Type =3D SxS1; + break; + + case V_ACPI_IO_PM1_CNT_S3: + Context->Sx.Type =3D SxS3; + break; + + case V_ACPI_IO_PM1_CNT_S4: + Context->Sx.Type =3D SxS4; + break; + + case V_ACPI_IO_PM1_CNT_S5: + Context->Sx.Type =3D 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 =3D=3D Context2->Sx.Type); +} + +/** + For each PCIE RP clear PME SCI status and disable SCI, then PCIEXP_WAKE_= STS from PMC. + This prevents platform from waking more than one time due to a single PC= IE wake event. + Normally it's up to OS to clear SCI statuses. But in a scenario where pl= atform wakes + and goes to S5 instead of booting to OS, the SCI status would remain set= and would trigger another wake. +**/ +VOID +ClearPcieSci ( + VOID + ) +{ + UINT32 MaxPorts; + UINT32 RpIndex; + UINT64 RpBase; + + MaxPorts =3D GetPchMaxPciePortNum (); + for (RpIndex =3D 0; RpIndex < MaxPorts; RpIndex++) { + RpBase =3D PchPcieBase (RpIndex); + if (PciSegmentRead16 (RpBase + PCI_VENDOR_ID_OFFSET) !=3D 0xFFFF) { + PciSegmentAnd8 ((RpBase + R_PCH_PCIE_CFG_MPC + 3), (UINT8)~((UINT8)(= B_PCH_PCIE_CFG_MPC_PMCE >> 24))); + PciSegmentWrite32 (RpBase + R_PCH_PCIE_CFG_SMSCS, B_PCH_PCIE_CFG_SMS= CS_PMCS); + } + } + IoWrite16 (mAcpiBaseAddr + R_ACPI_IO_PM1_STS, B_ACPI_IO_PM1_STS_PCIEXP_W= AKE_STS); +} + + +/** + When we get an SMI that indicates that we are transitioning to a sleep s= tate, + we need to actually transition to that state. We do this by disabling t= he + "SMI on sleep enable" feature, which generates an SMI when the operating= system + tries to put the system to sleep, and then physically putting the system= to sleep. + + +**/ +VOID +PchSmmSxGoToSleep ( + VOID + ) +{ + UINT32 Pm1Cnt; + + ClearPcieSci (); + + /// + /// Flush cache into memory before we go to sleep. It is necessary for S= 3 sleep + /// because we may update memory in SMM Sx sleep handlers -- the updates= are in cache now + /// + AsmWbinvd (); + + /// + /// Disable SMIs + /// + PchSmmClearSource (&mSxSourceDesc); + PchSmmDisableSource (&mSxSourceDesc); + + /// + /// Get Power Management 1 Control Register Value + /// + Pm1Cnt =3D IoRead32 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_PM1_CNT)); + + /// + /// Record S3 suspend performance data + /// + if ((Pm1Cnt & B_ACPI_IO_PM1_CNT_SLP_TYP) =3D=3D V_ACPI_IO_PM1_CNT_S3) { + /// + /// Report status code before goto S3 sleep + /// + REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PROGRESS_CODE_S3_SUSPEND_END); + + /// + /// Flush cache into memory before we go to sleep. + /// + AsmWbinvd (); + } + + /// + /// Now that SMIs are disabled, write to the SLP_EN bit again to trigger= the sleep + /// + Pm1Cnt |=3D B_ACPI_IO_PM1_CNT_SLP_EN; + + IoWrite32 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_PM1_CNT), Pm1Cnt); + + /// + /// Should only proceed if wake event is generated. + /// + if ((Pm1Cnt & B_ACPI_IO_PM1_CNT_SLP_TYP) =3D=3D V_ACPI_IO_PM1_CNT_S1) { + while (((IoRead16 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_PM1_STS))) & B_A= CPI_IO_PM1_STS_WAK) =3D=3D 0x0); + } else { + CpuDeadLoop (); + } + /// + /// The system just went to sleep. If the sleep state was S1, then code = execution will resume + /// here when the system wakes up. + /// + Pm1Cnt =3D IoRead32 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_PM1_CNT)); + + if ((Pm1Cnt & B_ACPI_IO_PM1_CNT_SCI_EN) =3D=3D 0) { + /// + /// An ACPI OS isn't present, clear the sleep information + /// + Pm1Cnt &=3D ~B_ACPI_IO_PM1_CNT_SLP_TYP; + Pm1Cnt |=3D V_ACPI_IO_PM1_CNT_S0; + + IoWrite32 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_PM1_CNT), Pm1Cnt); + } + + PchSmmClearSource (&mSxSourceDesc); + PchSmmEnableSource (&mSxSourceDesc); +} diff --git a/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/Pc= hSmmUsb.c b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/Pch= SmmUsb.c new file mode 100644 index 0000000000..061dbe4300 --- /dev/null +++ b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchSmmUsb= .c @@ -0,0 +1,231 @@ +/** @file + File to contain all the hardware specific stuff for the Smm USB dispatch= protocol. + + Copyright (c) 2019 Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include "PchSmmHelpers.h" +#include +#include +#include + + +GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mUsb1Legacy =3D { + 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 =3D { + 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 + } +}; + +typedef enum { + PchUsbControllerLpc0 =3D 0, + PchUsbControllerXhci, + PchUsbControllerTypeMax +} PCH_USB_CONTROLLER_TYPE; + +typedef struct { + UINT8 Function; + UINT8 Device; + PCH_USB_CONTROLLER_TYPE UsbConType; +} USB_CONTROLLER; + +GLOBAL_REMOVE_IF_UNREFERENCED USB_CONTROLLER mUsbControllersMap[] =3D { + { + PCI_FUNCTION_NUMBER_PCH_LPC, + PCI_DEVICE_NUMBER_PCH_LPC, + PchUsbControllerLpc0 + }, + { + PCI_FUNCTION_NUMBER_PCH_XHCI, + PCI_DEVICE_NUMBER_PCH_XHCI, + PchUsbControllerXhci + } +}; + +/** + Find the handle that best matches the input Device Path and return the U= SB 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 in= put Device Path + @exception EFI_UNSUPPORTED Invalid device Path table or can't find = any match USB device path + PCH_USB_CONTROLLER_TYPE The USB controll= er 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 =3D DevicePath; + Status =3D gBS->LocateDevicePath ( + &gEfiPciRootBridgeIoProtocolGuid, + &DevicePath, + &DeviceHandle + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + DevicePath =3D RemaingDevicePath; + + /// + /// Get first node: Acpi Node + /// + AcpiNode =3D (ACPI_HID_DEVICE_PATH *) RemaingDevicePath; + + if (AcpiNode->Header.Type !=3D ACPI_DEVICE_PATH || + AcpiNode->Header.SubType !=3D ACPI_DP || + DevicePathNodeLength (&AcpiNode->Header) !=3D sizeof (ACPI_HID_DEVIC= E_PATH) || + AcpiNode->HID !=3D EISA_PNP_ID (0x0A03) || + AcpiNode->UID !=3D 0 + ) { + return EFI_UNSUPPORTED; + } else { + /// + /// Get the next node: Pci Node + /// + RemaingDevicePath =3D NextDevicePathNode (RemaingDevicePath); + PciNode =3D (PCI_DEVICE_PATH *) RemaingDevicePath; + if (PciNode->Header.Type !=3D HARDWARE_DEVICE_PATH || + PciNode->Header.SubType !=3D HW_PCI_DP || + DevicePathNodeLength (&PciNode->Header) !=3D sizeof (PCI_DEVICE_PA= TH) + ) { + return EFI_UNSUPPORTED; + } + + for (UsbIndex =3D 0; UsbIndex < sizeof (mUsbControllersMap) / sizeof (= USB_CONTROLLER); UsbIndex++) { + if ((PciNode->Device =3D=3D mUsbControllersMap[UsbIndex].Device) && + (PciNode->Function =3D=3D mUsbControllersMap[UsbIndex].Function)= ) { + *Controller =3D 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 b= e 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 =3D DevicePathToSupportedController (Context->Usb.Device, &Contro= ller); + /// + /// 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/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/Pc= hxSmmHelpers.c b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Sm= m/PchxSmmHelpers.c new file mode 100644 index 0000000000..4ac00b831f --- /dev/null +++ b/Silicon/Intel/CoffeelakeSiliconPkg/Pch/PchSmiDispatcher/Smm/PchxSmmHe= lpers.c @@ -0,0 +1,764 @@ +/** @file + This driver is responsible for the registration of child drivers + and the abstraction of the PCH SMI sources. + + Copyright (c) 2019 Intel Corporation. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include "PchSmmHelpers.h" +#include +#include + +// +// Help handle porting bit shifts to IA-64. +// +#define BIT_ZERO 0x00000001 + +/** + Publish SMI Dispatch protocols. + + +**/ +VOID +PchSmmPublishDispatchProtocols ( + VOID + ) +{ + EFI_STATUS Status =3D EFI_SUCCESS; + UINTN Index; + // + // Install protocol interfaces. + // + for (Index =3D 0; Index < PCH_SMM_PROTOCOL_TYPE_MAX; Index++) { + Status =3D 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 successful= ly. +**/ +EFI_STATUS +PchSmmInitHardware ( + VOID + ) +{ + EFI_STATUS Status; + + // + // Clear all SMIs + // + PchSmmClearSmi (); + + Status =3D 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 =3D IoRead32 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_SMI_EN)); + + // + // Set the "global smi enable" bit + // + SmiEn |=3D 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. +**/ +VOID +PchSmmClearSmi ( + VOID + ) +{ + BOOLEAN EosSet; + BOOLEAN SciEn; + UINT32 Pm1Cnt; + UINT16 Pm1Sts; + UINT32 Gpe0Sts; + UINT32 SmiSts; + UINT16 DevActSts; + UINT16 Tco1Sts; + + Gpe0Sts =3D 0; + // + // Determine whether an ACPI OS is present (via the SCI_EN bit) + // + Pm1Cnt =3D IoRead32 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_PM1_CNT)); + SciEn =3D (BOOLEAN) ((Pm1Cnt & B_ACPI_IO_PM1_CNT_SCI_EN) =3D=3D B_ACPI_= IO_PM1_CNT_SCI_EN); + if (!SciEn) { + // + // Clear any SMIs that double as SCIs (when SCI_EN=3D=3D0) + // + Pm1Sts =3D IoRead16 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_PM1_STS)); + Gpe0Sts =3D IoRead32 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_GPE0_STS_127= _96)); + + Pm1Sts |=3D + ( + B_ACPI_IO_PM1_STS_WAK | + B_ACPI_IO_PM1_STS_PRBTNOR | + B_ACPI_IO_PM1_STS_RTC | + B_ACPI_IO_PM1_STS_PWRBTN | + B_ACPI_IO_PM1_STS_GBL | + B_ACPI_IO_PM1_STS_TMROF + ); + + Gpe0Sts &=3D (UINT32)~(B_ACPI_IO_GPE0_STS_127_96_WADT); + + IoWrite16 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_PM1_STS), (UINT16) Pm1St= s); + IoWrite32 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_GPE0_STS_127_96), (UINT3= 2) Gpe0Sts); + } + // + // Clear all SMIs that are unaffected by SCI_EN + // + SmiSts =3D IoRead32 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_SMI_STS))= ; + DevActSts =3D IoRead16 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_DEVACT_ST= S)); + Tco1Sts =3D IoRead16 ((UINTN) (mTcoBaseAddr + R_TCO_IO_TCO1_STS)); + + SmiSts |=3D + ( + B_ACPI_IO_SMI_STS_SMBUS | + B_ACPI_IO_SMI_STS_PERIODIC | + B_ACPI_IO_SMI_STS_TCO | + B_ACPI_IO_SMI_STS_MCSMI | + B_ACPI_IO_SMI_STS_SWSMI_TMR | + B_ACPI_IO_SMI_STS_APM | + B_ACPI_IO_SMI_STS_ON_SLP_EN | + B_ACPI_IO_SMI_STS_BIOS + ); + DevActSts |=3D + ( + B_ACPI_IO_DEVACT_STS_KBC | + B_ACPI_IO_DEVACT_STS_PIRQDH | + B_ACPI_IO_DEVACT_STS_PIRQCG | + B_ACPI_IO_DEVACT_STS_PIRQBF | + B_ACPI_IO_DEVACT_STS_PIRQAE + ); + Tco1Sts |=3D + ( + 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 + ); + + GpioClearAllGpiSmiSts (); + + IoWrite16 ((UINTN) (mTcoBaseAddr + R_TCO_IO_TCO1_STS), Tco1Sts); + + // + // We do not want to write 1 to clear INTRD_DET bit. + // + IoWrite16 ((UINTN) (mTcoBaseAddr + R_TCO_IO_TCO2_STS), (UINT16) ~B_TCO_I= O_TCO2_STS_INTRD_DET); + + IoWrite32 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_SMI_STS), SmiSts); + + IoWrite16 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_DEVACT_STS), DevActSts); + + // + // Try to clear the EOS bit. ASSERT on an error + // + EosSet =3D PchSmmSetAndCheckEos (); + ASSERT (EosSet); +} + +/** + 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 =3D IoRead32 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_SMI_EN)); + + // + // Reset the PCH to generate subsequent SMIs + // + SmiEn |=3D B_ACPI_IO_SMI_EN_EOS; + + IoWrite32 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_SMI_EN), SmiEn); + + // + // Double check that the assert worked + // + SmiEn =3D IoRead32 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_SMI_EN)); + + // + // Return TRUE if EOS is set correctly + // + if ((SmiEn & B_ACPI_IO_SMI_EN_EOS) =3D=3D 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 =3D IoRead32 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_PM1_CNT)); + SciEn =3D (BOOLEAN) ((Pm1Cnt & B_ACPI_IO_PM1_CNT_SCI_EN) =3D=3D 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 I= A-32 dependent, though. + + @param[in] BitDesc The struct that includes register addres= s, 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 !=3D NULL); + ASSERT (!IS_BIT_DESC_NULL (*BitDesc)); + + RegSize =3D 0; + Register =3D 0; + ShiftCount =3D 0; + BitWasOne =3D FALSE; + + switch (BitDesc->Reg.Type) { + + case ACPI_ADDR_TYPE: + case TCO_ADDR_TYPE: + if (BitDesc->Reg.Type =3D=3D ACPI_ADDR_TYPE) { + RegisterOffset =3D BitDesc->Reg.Data.acpi; + BaseAddr =3D mAcpiBaseAddr; + } else { + RegisterOffset =3D BitDesc->Reg.Data.tco; + BaseAddr =3D 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 =3D SMM_IO_UINT8; + break; + + case 2: + RegSize =3D SMM_IO_UINT16; + break; + + case 4: + RegSize =3D SMM_IO_UINT32; + break; + + case 8: + RegSize =3D 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 !=3D 0x0) && ((BaseAddr & 0x1) !=3D 0x1)); + + ShiftCount =3D 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 =3D=3D SMM_IO_UINT64) { + RegSize =3D SMM_IO_UINT32; + // + // If the operation is for high 32 bits + // + if (BitDesc->Bit >=3D 32) { + RegisterOffset +=3D 4; + ShiftCount -=3D 32; + } + } + + Status =3D gSmst->SmmIo.Io.Read ( + &gSmst->SmmIo, + RegSize, + BaseAddr + RegisterOffset, + 1, + &Register + ); + ASSERT_EFI_ERROR (Status); + + if ((Register & (LShiftU64 (BIT_ZERO, ShiftCount))) !=3D 0) { + BitWasOne =3D TRUE; + } else { + BitWasOne =3D 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 =3D (UINT64) MmioRead8 ((UINTN) BitDesc->Reg.Data.Mmio)= ; + break; + + case 2: + Register =3D (UINT64) MmioRead16 ((UINTN) BitDesc->Reg.Data.Mmio= ); + break; + + case 4: + Register =3D (UINT64) MmioRead32 ((UINTN) BitDesc->Reg.Data.Mmio= ); + break; + + case 8: + Register =3D (UINT64) MmioRead32 ((UINTN) B= itDesc->Reg.Data.Mmio); + *((UINT32 *) (&Register) + 1) =3D MmioRead32 ((UINTN) BitDesc->R= eg.Data.Mmio + 4); + break; + + default: + // + // Unsupported or invalid register size + // + ASSERT (FALSE); + break; + } + + Register =3D Register & (LShiftU64 (BIT0, BitDesc->Bit)); + if (Register) { + BitWasOne =3D TRUE; + } else { + BitWasOne =3D FALSE; + } + break; + + case PCIE_ADDR_TYPE: + PciBus =3D BitDesc->Reg.Data.pcie.Fields.Bus; + PciDev =3D BitDesc->Reg.Data.pcie.Fields.Dev; + PciFun =3D BitDesc->Reg.Data.pcie.Fields.Fnc; + PciReg =3D BitDesc->Reg.Data.pcie.Fields.Reg; + PciBaseAddress =3D PCI_SEGMENT_LIB_ADDRESS (DEFAULT_PCI_SEGMENT_NUMB= ER_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 =3D (UINT64) PciSegmentRead8 (PciBaseAddress + PciReg); + break; + + case 2: + Register =3D (UINT64) PciSegmentRead16 (PciBaseAddress + PciReg)= ; + break; + + case 4: + Register =3D (UINT64) PciSegmentRead32 (PciBaseAddress + PciReg)= ; + break; + + default: + // + // Unsupported or invalid register size + // + ASSERT (FALSE); + break; + } + + if ((Register & (LShiftU64 (BIT_ZERO, BitDesc->Bit))) !=3D 0) { + BitWasOne =3D TRUE; + } else { + BitWasOne =3D FALSE; + } + break; + + case PCR_ADDR_TYPE: + // + // Read the register, and it with the bit to read + // + switch (BitDesc->SizeInBytes) { + case 1: + Register =3D PchPcrRead8 (BitDesc->Reg.Data.Pcr.Fields.Pid, Bit= Desc->Reg.Data.Pcr.Fields.Offset); + break; + + case 2: + Register =3D PchPcrRead16 (BitDesc->Reg.Data.Pcr.Fields.Pid, Bit= Desc->Reg.Data.Pcr.Fields.Offset); + break; + + case 4: + Register =3D PchPcrRead32 (BitDesc->Reg.Data.Pcr.Fields.Pid, Bit= Desc->Reg.Data.Pcr.Fields.Offset); + break; + + default: + // + // Unsupported or invalid register size + // + ASSERT (FALSE); + break; + } + + Register =3D Register & (LShiftU64 (BIT0, BitDesc->Bit)); + if (Register) { + BitWasOne =3D TRUE; + } else { + BitWasOne =3D 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 addres= s, 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 writ= e 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 !=3D NULL); + ASSERT (!IS_BIT_DESC_NULL (*BitDesc)); + + RegSize =3D 0; + Register =3D 0; + + if (WriteClear) { + AndVal =3D LShiftU64 (BIT_ZERO, BitDesc->Bit); + } else { + AndVal =3D ~(LShiftU64 (BIT_ZERO, BitDesc->Bit)); + } + + OrVal =3D (LShiftU64 ((UINT32) ValueToWrite, BitDesc->Bit)); + + switch (BitDesc->Reg.Type) { + + case ACPI_ADDR_TYPE: + case TCO_ADDR_TYPE: + if (BitDesc->Reg.Type =3D=3D ACPI_ADDR_TYPE) { + RegisterOffset =3D BitDesc->Reg.Data.acpi; + BaseAddr =3D mAcpiBaseAddr; + } else { + RegisterOffset =3D BitDesc->Reg.Data.tco; + BaseAddr =3D 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 =3D SMM_IO_UINT8; + break; + + case 2: + RegSize =3D SMM_IO_UINT16; + break; + + case 4: + RegSize =3D SMM_IO_UINT32; + break; + + case 8: + RegSize =3D 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 !=3D 0x0) && ((BaseAddr & 0x1) !=3D 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 =3D=3D SMM_IO_UINT64) { + RegSize =3D SMM_IO_UINT32; + // + // If the operation is for high 32 bits + // + if (BitDesc->Bit >=3D 32) { + RegisterOffset +=3D 4; + + if (WriteClear) { + AndVal =3D LShiftU64 (BIT_ZERO, BitDesc->Bit - 32); + } else { + AndVal =3D ~(LShiftU64 (BIT_ZERO, BitDesc->Bit - 32)); + } + + OrVal =3D LShiftU64 ((UINT32) ValueToWrite, BitDesc->Bit - 32); + } + } + + Status =3D gSmst->SmmIo.Io.Read ( + &gSmst->SmmIo, + RegSize, + BaseAddr + RegisterOffset, + 1, + &Register + ); + ASSERT_EFI_ERROR (Status); + + Register &=3D AndVal; + Register |=3D OrVal; + + Status =3D 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 =3D (UINT64) MmioRead32 ((UINTN) B= itDesc->Reg.Data.Mmio); + *((UINT32 *) (&Register) + 1) =3D MmioRead32 ((UINTN) BitDesc->R= eg.Data.Mmio + 4); + Register &=3D AndVal; + Register |=3D 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 =3D BitDesc->Reg.Data.pcie.Fields.Bus; + PciDev =3D BitDesc->Reg.Data.pcie.Fields.Dev; + PciFun =3D BitDesc->Reg.Data.pcie.Fields.Fnc; + PciReg =3D BitDesc->Reg.Data.pcie.Fields.Reg; + PciBaseAddress =3D PCI_SEGMENT_LIB_ADDRESS (DEFAULT_PCI_SEGMENT_NUMB= ER_PCH, PciBus, PciDev, PciFun, 0); + switch (BitDesc->SizeInBytes) { + + case 0: + // + // Chances are that this field didn't get initialized -- check y= our 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.Pi= d, (UINT16) BitDesc->Reg.Data.Pcr.Fields.Offset, (UINT8) AndVal, (UINT8) = OrVal); + break; + + case 2: + PchPcrAndThenOr16 ((PCH_SBI_PID) BitDesc->Reg.Data.Pcr.Fields.Pi= d, (UINT16) BitDesc->Reg.Data.Pcr.Fields.Offset, (UINT16) AndVal, (UINT16) = OrVal); + break; + + case 4: + PchPcrAndThenOr32 ((PCH_SBI_PID) BitDesc->Reg.Data.Pcr.Fields.Pi= d, (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; + } +} --=20 2.16.2.windows.1