/** @file
  eSPI SMI implementation

Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "PchSmmEspi.h"

#define PCH_ESPI_SMI_DISPATCH_GUID \
  {0xcfde92ca, 0xd104, 0x4f75, {0x85, 0xde, 0xab, 0x56, 0x5e, 0x2f, 0x52, 0xf0}}

EFI_GUID gPchEspiSmiDispatchGuid = PCH_ESPI_SMI_DISPATCH_GUID;

GLOBAL_REMOVE_IF_UNREFERENCED ESPI_SMI_INSTANCE mEspiSmiInstance = {
  //
  // 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[EspiSmiTypeMax] = {
  //
  // BiosWrProtect
  //
  {
    {
      PCIE_ADDR_TYPE,
      { (
        (DEFAULT_PCI_BUS_NUMBER_PCH << 24) |
        (PCI_DEVICE_NUMBER_PCH_LPC << 19) |
        (PCI_FUNCTION_NUMBER_PCH_LPC << 16) |
        R_PCH_ESPI_PCBC
      ) }
    },
    //
    // SourceIsActiveAndMask and SourceIsActiveValue
    //
    B_PCH_ESPI_PCBC_BWPDS | B_PCH_ESPI_PCBC_LE,
    B_PCH_ESPI_PCBC_BWPDS | B_PCH_ESPI_PCBC_LE,
    //
    // ClearStatusAndMask and ClearStatusOrMask
    //
    (UINT32) ~B_PCH_ESPI_PCBC_BWRS,
    B_PCH_ESPI_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_PCH_ESPI_PCBC
      ) }
    },
    B_PCH_ESPI_PCBC_BWRS | B_PCH_ESPI_PCBC_BWRE,
    B_PCH_ESPI_PCBC_BWRS | B_PCH_ESPI_PCBC_BWRE,
    (UINT32) ~B_PCH_ESPI_PCBC_BWPDS,
    B_PCH_ESPI_PCBC_BWRS
  },
  //
  // PcNonFatalErr
  //
  {
    {
      PCR_ADDR_TYPE,
      {PCH_PCR_ADDRESS (PID_ESPISPI, R_PCH_PCR_ESPI_PCERR_SLV0) }
    },
    (B_PCH_PCR_ESPI_XERR_XNFES | B_PCH_PCR_ESPI_XERR_XNFEE),
    (B_PCH_PCR_ESPI_XERR_XNFES | (V_PCH_PCR_ESPI_XERR_XNFEE_SMI << N_PCH_PCR_ESPI_XERR_XNFEE)),
    (UINT32) ~(B_PCH_PCR_ESPI_PCERR_SLV0_PCURD | B_PCH_PCR_ESPI_XERR_XFES),
    B_PCH_PCR_ESPI_XERR_XNFES
  },
  //
  // PcFatalErr
  //
  {
    {
      PCR_ADDR_TYPE,
      {PCH_PCR_ADDRESS (PID_ESPISPI, R_PCH_PCR_ESPI_PCERR_SLV0) }
    },
    (B_PCH_PCR_ESPI_XERR_XFES | B_PCH_PCR_ESPI_XERR_XFEE),
    (B_PCH_PCR_ESPI_XERR_XFES | (V_PCH_PCR_ESPI_XERR_XFEE_SMI << N_PCH_PCR_ESPI_XERR_XFEE)),
    (UINT32) ~(B_PCH_PCR_ESPI_PCERR_SLV0_PCURD | B_PCH_PCR_ESPI_XERR_XNFES),
    B_PCH_PCR_ESPI_XERR_XFES
  },
  //
  // VwNonFatalErr
  //
  {
    {
      PCR_ADDR_TYPE,
      {PCH_PCR_ADDRESS (PID_ESPISPI, R_PCH_PCR_ESPI_VWERR_SLV0) }
    },
    (B_PCH_PCR_ESPI_XERR_XNFES | B_PCH_PCR_ESPI_XERR_XNFEE),
    (B_PCH_PCR_ESPI_XERR_XNFES | (V_PCH_PCR_ESPI_XERR_XNFEE_SMI << N_PCH_PCR_ESPI_XERR_XNFEE)),
    (UINT32) ~B_PCH_PCR_ESPI_XERR_XFES,
    B_PCH_PCR_ESPI_XERR_XNFES
  },
  //
  // VwFatalErr
  //
  {
    {
      PCR_ADDR_TYPE,
      {PCH_PCR_ADDRESS (PID_ESPISPI, R_PCH_PCR_ESPI_VWERR_SLV0) }
    },
    (B_PCH_PCR_ESPI_XERR_XFES | B_PCH_PCR_ESPI_XERR_XFEE),
    (B_PCH_PCR_ESPI_XERR_XFES | (V_PCH_PCR_ESPI_XERR_XFEE_SMI << N_PCH_PCR_ESPI_XERR_XFEE)),
    (UINT32) ~B_PCH_PCR_ESPI_XERR_XNFES,
    B_PCH_PCR_ESPI_XERR_XFES
  },
  //
  // FlashNonFatalErr
  //
  {
    {
      PCR_ADDR_TYPE,
      {PCH_PCR_ADDRESS (PID_ESPISPI, R_PCH_PCR_ESPI_FCERR_SLV0) }
    },
    (B_PCH_PCR_ESPI_XERR_XNFES | B_PCH_PCR_ESPI_XERR_XNFEE),
    (B_PCH_PCR_ESPI_XERR_XNFES | (V_PCH_PCR_ESPI_XERR_XNFEE_SMI << N_PCH_PCR_ESPI_XERR_XNFEE)),
    (UINT32) ~B_PCH_PCR_ESPI_XERR_XFES,
    B_PCH_PCR_ESPI_XERR_XNFES
  },
  //
  // FlashFatalErr
  //
  {
    {
      PCR_ADDR_TYPE,
      {PCH_PCR_ADDRESS (PID_ESPISPI, R_PCH_PCR_ESPI_FCERR_SLV0) }
    },
    (B_PCH_PCR_ESPI_XERR_XFES | B_PCH_PCR_ESPI_XERR_XFEE),
    (B_PCH_PCR_ESPI_XERR_XFES | (V_PCH_PCR_ESPI_XERR_XFEE_SMI << N_PCH_PCR_ESPI_XERR_XFEE)),
    (UINT32) ~B_PCH_PCR_ESPI_XERR_XNFES,
    B_PCH_PCR_ESPI_XERR_XFES
  },
  //
  // LnkType1Err
  //
  {
    {
      PCR_ADDR_TYPE,
      {PCH_PCR_ADDRESS (PID_ESPISPI, R_PCH_PCR_ESPI_LNKERR_SLV0) }
    },
    B_PCH_PCR_ESPI_LNKERR_SLV0_LFET1S | B_PCH_PCR_ESPI_LNKERR_SLV0_LFET1E,
    B_PCH_PCR_ESPI_LNKERR_SLV0_LFET1S | (V_PCH_PCR_ESPI_LNKERR_SLV0_LFET1E_SMI << N_PCH_PCR_ESPI_LNKERR_SLV0_LFET1E),
    (UINT32) ~B_PCH_PCR_ESPI_LNKERR_SLV0_SLCRR,
    B_PCH_PCR_ESPI_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
  )
{
  EFI_STATUS  Status;
  UINTN       PciBaseAddress;

  switch (EspiSmiType) {
    case BiosWrProtect:
      PciBaseAddress = MmPciBase (
                         DEFAULT_PCI_BUS_NUMBER_PCH,
                         PCI_DEVICE_NUMBER_PCH_LPC,
                         PCI_FUNCTION_NUMBER_PCH_LPC
                         );
      MmioAndThenOr32 (
        PciBaseAddress + R_PCH_ESPI_PCBC,
        (UINT32) ~(B_PCH_ESPI_PCBC_BWRS | B_PCH_ESPI_PCBC_BWPDS),
        B_PCH_ESPI_PCBC_LE
        );
      break;
    case BiosWrReport:
      PciBaseAddress = MmPciBase (
                         DEFAULT_PCI_BUS_NUMBER_PCH,
                         PCI_DEVICE_NUMBER_PCH_LPC,
                         PCI_FUNCTION_NUMBER_PCH_LPC
                         );
      MmioAndThenOr32 (
        PciBaseAddress + R_PCH_ESPI_PCBC,
        (UINT32) ~(B_PCH_ESPI_PCBC_BWRS | B_PCH_ESPI_PCBC_BWPDS),
        B_PCH_ESPI_PCBC_BWRE
        );
      break;
    case PcNonFatalErr:
      Status = PchPcrAndThenOr32 (
                 PID_ESPISPI,
                 (UINT16) R_PCH_PCR_ESPI_PCERR_SLV0,
                 (UINT32) ~(B_PCH_PCR_ESPI_PCERR_SLV0_PCURD | B_PCH_PCR_ESPI_XERR_XNFES | B_PCH_PCR_ESPI_XERR_XFES),
                 B_PCH_PCR_ESPI_XERR_XNFEE);
      ASSERT_EFI_ERROR (Status);
      break;

    case PcFatalErr:
      Status = PchPcrAndThenOr32 (
                 PID_ESPISPI,
                 (UINT16) R_PCH_PCR_ESPI_PCERR_SLV0,
                 (UINT32) ~(B_PCH_PCR_ESPI_PCERR_SLV0_PCURD | B_PCH_PCR_ESPI_XERR_XNFES | B_PCH_PCR_ESPI_XERR_XFES),
                 B_PCH_PCR_ESPI_XERR_XFEE);
      ASSERT_EFI_ERROR (Status);
      break;

    case VwNonFatalErr:
      Status = PchPcrAndThenOr32 (
                 PID_ESPISPI,
                 (UINT16) R_PCH_PCR_ESPI_VWERR_SLV0,
                 (UINT32) ~(B_PCH_PCR_ESPI_XERR_XNFES | B_PCH_PCR_ESPI_XERR_XFES),
                 B_PCH_PCR_ESPI_XERR_XNFEE);
      ASSERT_EFI_ERROR (Status);
      break;

    case VwFatalErr:
      Status = PchPcrAndThenOr32 (
                 PID_ESPISPI,
                 (UINT16) R_PCH_PCR_ESPI_VWERR_SLV0,
                 (UINT32) ~(B_PCH_PCR_ESPI_XERR_XNFES | B_PCH_PCR_ESPI_XERR_XFES),
                 B_PCH_PCR_ESPI_XERR_XFEE);
      ASSERT_EFI_ERROR (Status);
      break;

    case FlashNonFatalErr:
      Status = PchPcrAndThenOr32 (
                 PID_ESPISPI,
                 (UINT16) R_PCH_PCR_ESPI_FCERR_SLV0,
                 (UINT32) ~(B_PCH_PCR_ESPI_XERR_XNFES | B_PCH_PCR_ESPI_XERR_XFES),
                 B_PCH_PCR_ESPI_XERR_XNFEE);
      ASSERT_EFI_ERROR (Status);
      break;

    case FlashFatalErr:
      Status = PchPcrAndThenOr32 (
                 PID_ESPISPI,
                 (UINT16) R_PCH_PCR_ESPI_FCERR_SLV0,
                 (UINT32) ~(B_PCH_PCR_ESPI_XERR_XNFES | B_PCH_PCR_ESPI_XERR_XFES),
                 B_PCH_PCR_ESPI_XERR_XFEE);
      ASSERT_EFI_ERROR (Status);
      break;

    case LnkType1Err:
      Status = PchPcrAndThenOr32 (
                 PID_ESPISPI,
                 (UINT16) R_PCH_PCR_ESPI_LNKERR_SLV0,
                 (UINT32) ~(B_PCH_PCR_ESPI_LNKERR_SLV0_SLCRR | B_PCH_PCR_ESPI_LNKERR_SLV0_LFET1S),
                 B_PCH_PCR_ESPI_LNKERR_SLV0_LFET1E);
      ASSERT_EFI_ERROR (Status);
      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
  )
{
  EFI_STATUS Status;

  switch (EspiSmiType) {
    case BiosWrProtect:
    case BiosWrReport:
      DEBUG ((DEBUG_ERROR, "Bit is write lock, thus BWRE/BWPDS source cannot be disabled \n"));
      ASSERT (FALSE);
      break;
    case PcNonFatalErr:
      Status = PchPcrAndThenOr32 (
                 PID_ESPISPI,
                 (UINT16) R_PCH_PCR_ESPI_PCERR_SLV0,
                 (UINT32) ~(B_PCH_PCR_ESPI_PCERR_SLV0_PCURD | B_PCH_PCR_ESPI_XERR_XNFES | B_PCH_PCR_ESPI_XERR_XFES | B_PCH_PCR_ESPI_XERR_XNFEE),
                 0);
      ASSERT_EFI_ERROR (Status);
      break;

    case PcFatalErr:
      Status = PchPcrAndThenOr32 (
                 PID_ESPISPI,
                 (UINT16) R_PCH_PCR_ESPI_PCERR_SLV0,
                 (UINT32) ~(B_PCH_PCR_ESPI_PCERR_SLV0_PCURD | B_PCH_PCR_ESPI_XERR_XNFES | B_PCH_PCR_ESPI_XERR_XFES | B_PCH_PCR_ESPI_XERR_XFEE),
                 0);
      ASSERT_EFI_ERROR (Status);
      break;

    case VwNonFatalErr:
      Status = PchPcrAndThenOr32 (
                 PID_ESPISPI,
                 (UINT16) R_PCH_PCR_ESPI_VWERR_SLV0,
                 (UINT32) ~(B_PCH_PCR_ESPI_XERR_XNFES | B_PCH_PCR_ESPI_XERR_XFES | B_PCH_PCR_ESPI_XERR_XNFEE),
                 0);
      ASSERT_EFI_ERROR (Status);
      break;

    case VwFatalErr:
      Status = PchPcrAndThenOr32 (
                 PID_ESPISPI,
                 (UINT16) R_PCH_PCR_ESPI_VWERR_SLV0,
                 (UINT32) ~(B_PCH_PCR_ESPI_XERR_XNFES | B_PCH_PCR_ESPI_XERR_XFES | B_PCH_PCR_ESPI_XERR_XFEE),
                 0);
      ASSERT_EFI_ERROR (Status);
      break;

    case FlashNonFatalErr:
      Status = PchPcrAndThenOr32 (
                 PID_ESPISPI,
                 (UINT16) R_PCH_PCR_ESPI_FCERR_SLV0,
                 (UINT32) ~(B_PCH_PCR_ESPI_XERR_XNFES | B_PCH_PCR_ESPI_XERR_XFES | B_PCH_PCR_ESPI_XERR_XNFEE),
                 0);
      ASSERT_EFI_ERROR (Status);
      break;

    case FlashFatalErr:
      Status = PchPcrAndThenOr32 (
                 PID_ESPISPI,
                 (UINT16) R_PCH_PCR_ESPI_FCERR_SLV0,
                 (UINT32) ~(B_PCH_PCR_ESPI_XERR_XNFES | B_PCH_PCR_ESPI_XERR_XFES | B_PCH_PCR_ESPI_XERR_XFEE),
                 0);
      ASSERT_EFI_ERROR (Status);
      break;

    case LnkType1Err:
      Status = PchPcrAndThenOr32 (
                 PID_ESPISPI,
                 (UINT16) R_PCH_PCR_ESPI_LNKERR_SLV0,
                 (UINT32) ~(B_PCH_PCR_ESPI_LNKERR_SLV0_SLCRR | B_PCH_PCR_ESPI_LNKERR_SLV0_LFET1S),
                 0);
      ASSERT_EFI_ERROR (Status);
      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
  )
{
  EFI_STATUS              Status;
  UINT32                  PciBus;
  UINT32                  PciDev;
  UINT32                  PciFun;
  UINT32                  PciReg;
  UINT32                  PciBaseAddress;
  CONST ESPI_DESCRIPTOR   *Desc;

  Desc = &mEspiDescriptor[EspiSmiType];

  switch (Desc->Address.Type) {
    case PCIE_ADDR_TYPE:
      PciBus  = Desc->Address.Data.pcie.Fields.Bus;
      PciDev  = Desc->Address.Data.pcie.Fields.Dev;
      PciFun  = Desc->Address.Data.pcie.Fields.Fnc;
      PciReg  = Desc->Address.Data.pcie.Fields.Reg;
      PciBaseAddress = (UINT32) MmPciBase (PciBus, PciDev, PciFun);
      MmioAndThenOr32 (PciBaseAddress + PciReg, Desc->ClearStatusAndMask, Desc->ClearStatusOrMask);
      break;
    case PCR_ADDR_TYPE:
      Status = PchPcrAndThenOr32 (
                 Desc->Address.Data.Pcr.Fields.Pid,
                 Desc->Address.Data.Pcr.Fields.Offset,
                 Desc->ClearStatusAndMask,
                 Desc->ClearStatusOrMask
                 );
      ASSERT_EFI_ERROR (Status);
      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
  )
{
  EFI_STATUS              Status;
  BOOLEAN                 Active;
  UINT32                  PciBus;
  UINT32                  PciDev;
  UINT32                  PciFun;
  UINT32                  PciReg;
  UINT32                  PciBaseAddress;
  UINT32                  Data32;
  CONST ESPI_DESCRIPTOR   *Desc;

  Desc = &mEspiDescriptor[EspiSmiType];

  Active = FALSE;
  switch (Desc->Address.Type) {
    case PCIE_ADDR_TYPE:
      PciBus  = Desc->Address.Data.pcie.Fields.Bus;
      PciDev  = Desc->Address.Data.pcie.Fields.Dev;
      PciFun  = Desc->Address.Data.pcie.Fields.Fnc;
      PciReg  = Desc->Address.Data.pcie.Fields.Reg;
      PciBaseAddress = (UINT32) MmPciBase (PciBus, PciDev, PciFun);
      Data32 = MmioRead32 (PciBaseAddress + PciReg);
      break;

    case PCR_ADDR_TYPE:
      Status = PchPcrRead32 (
                 Desc->Address.Data.Pcr.Fields.Pid,
                 Desc->Address.Data.Pcr.Fields.Offset,
                 &Data32
                 );
      ASSERT_EFI_ERROR (Status);
      break;

    default:
      Data32 = 0;
      DEBUG ((DEBUG_ERROR, "Address type for eSPI SMI is invalid \n"));
      ASSERT (FALSE);
      break;
  }

  if ((Data32 & Desc->SourceIsActiveAndMask) == Desc->SourceIsActiveValue) {
    Active = 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 determine 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 = 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  = DispatchFunction;
  Record->Signature = ESPI_SMI_RECORD_SIGNATURE;
  Record->EspiSmiType = EspiSmiType;

  InsertTailList (&mEspiSmiInstance.CallbackDataBase[EspiSmiType], &Record->Link);
  EspiSmiClearStatus (EspiSmiType);
  EspiSmiEnableSource (EspiSmiType);

  ++mEspiSmiInstance.EspiSmiEventCounter[EspiSmiType];

  *DispatchHandle = (EFI_HANDLE) (&Record->Link);

  SmiHandlerProfileRegisterHandler (&gPchEspiSmiDispatchGuid, (EFI_SMM_HANDLER_ENTRY_POINT2)DispatchFunction, (UINTN)RETURN_ADDRESS (0), &EspiSmiType, sizeof(EspiSmiType));

  return EFI_SUCCESS;
}

/**
  This callback is registered to PchSmiDispatch

  @param[in]  DispatchHandle  Used to determine which source have been triggered
**/
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 = DATABASE_RECORD_FROM_LINK (DispatchHandle);

  if (PchSmiRecord->PchSmiType == PchTcoSmiLpcBiosWpType) {
    EspiTopLevelType = EspiBiosWrProtect;
  } else if (PchSmiRecord->PchSmiType == PchSmiSerialIrqType) {
    EspiTopLevelType = EspiSerialIrq;
  } else {
    DEBUG ((DEBUG_ERROR, "EspiSmiCallback was dispatched with a wrong DispatchHandle"));
    ASSERT (FALSE);
    return;
  }

  for (EspiSmiType = mEspiSmiInstance.Barrier[EspiTopLevelType].Start; EspiSmiType <= mEspiSmiInstance.Barrier[EspiTopLevelType].End; ++EspiSmiType) {
    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 = GetFirstNode (&mEspiSmiInstance.CallbackDataBase[EspiSmiType]);

      while (!IsNull (&mEspiSmiInstance.CallbackDataBase[EspiSmiType], LinkInDb)) {
        RecordInDb = ESPI_RECORD_FROM_LINK (LinkInDb);

        //
        // RecordInDb->Link might be removed (unregistered) by Callback function, and then the
        // system will hang in ASSERT() while calling GetNextNode().
        // To prevent the issue, we need to get next record in DB here (before Callback function).
        //
        LinkInDb = GetNextNode (&mEspiSmiInstance.CallbackDataBase[EspiSmiType], &RecordInDb->Link);

        //
        // Callback
        //
        if (RecordInDb->Callback != NULL) {
          RecordInDb->Callback ((EFI_HANDLE) &RecordInDb->Link);
        } else {
          ASSERT (FALSE);
        }
      }
    }
    //
    // Finish walking the linked list for the EspiSmiType, so clear status
    //
    EspiSmiClearStatus (EspiSmiType);
  }
}

//
// EspiBiosWp srcdesc
//
PCH_SMM_SOURCE_DESC mSrcDescEspiBiosWp = {
  PCH_SMM_NO_FLAGS,
  {
    {
      {
        ACPI_ADDR_TYPE,
        {R_PCH_SMI_EN}
      },
      S_PCH_SMI_EN,
      N_PCH_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_PCH_ESPI_PCBC
        ) }
      },
      S_PCH_ESPI_PCBC,
      N_PCH_ESPI_PCBC_LE
    }
  },
  {
    {
      {
        PCIE_ADDR_TYPE,
        { (
          (DEFAULT_PCI_BUS_NUMBER_PCH << 24) |
          (PCI_DEVICE_NUMBER_PCH_LPC << 19) |
          (PCI_FUNCTION_NUMBER_PCH_LPC << 16) |
          R_PCH_ESPI_PCBC
        ) }
      },
      S_PCH_ESPI_PCBC,
      N_PCH_ESPI_PCBC_BWPDS
    }
  },
  {
    {
      ACPI_ADDR_TYPE,
      {R_PCH_SMI_STS}
    },
    S_PCH_SMI_STS,
    N_PCH_SMI_STS_TCO
  }
};

/**
  This function will register EspiSmiCallback with mSrcDescEspiBiosWp source decriptor
  This function make sure there is only one BIOS WP SMI handler is registered.
  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 the 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] == NULL) {
    Status = PchSmiRecordInsert (
               &gPchTcoSmiDispatchProtocolGuid,
               &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 = DATABASE_RECORD_FROM_LINK (mEspiSmiInstance.PchSmiEspiHandle[EspiBiosWrProtect]);
    Record->ClearSource = PchTcoSmiClearSource;
  }

  return EFI_SUCCESS;
}

/**
  This function will register EspiSmiCallback with mSrcDescSerialIrq source decriptor
  This function make sure there is only one Serial IRQ SMI handler is registered.
  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 the 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] == NULL) {
    Status = PchSmiRecordInsert (
               &gPchSmiDispatchProtocolGuid,
               &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 EspiType;

  //
  // InitializeListHead for mEspiSmiInstance.CallBackDataBase[EspiTopLevelTypeMax]
  //
  for (EspiType = 0; EspiType < EspiSmiTypeMax; ++EspiType) {
    InitializeListHead (&mEspiSmiInstance.CallbackDataBase[EspiType]);
  }

  //
  // Install EfiEspiSmiDispatchProtocol
  //
  Status = gSmst->SmmInstallProtocolInterface (
                    &mEspiSmiInstance.Handle,
                    &gPchEspiSmiDispatchProtocolGuid,
                    EFI_NATIVE_INTERFACE,
                    &mEspiSmiInstance.PchEspiSmiDispatchProtocol
                    );
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "Fail to install eSPI SMI Dispatch Protocol \n"));
    ASSERT (FALSE);
    return Status;
  }

  return EFI_SUCCESS;
}

/**
  eSPI SMI Dispatch Protocol instance to register a BIOS Write Protect 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
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 == TRUE) {
    DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock event has been triggered! \n"));
    return EFI_ACCESS_DENIED;
  }

  Status = RegisterBiosWrProtectIfNull ();
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Insert a record
  //
  return InsertEspiRecord (DispatchFunction, BiosWrProtect, DispatchHandle);
}

/**
  eSPI SMI Dispatch Protocol instance to register a BIOS Write Report 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
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 == TRUE) {
    DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock event has been triggered! \n"));
    return EFI_ACCESS_DENIED;
  }

  Status = RegisterSerialIrqIfNull ();
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Insert a record
  //
  return InsertEspiRecord (DispatchFunction, BiosWrReport, 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
  )
{
  EFI_STATUS Status;

  //
  // Return access denied if the SmmReadyToLock event has been triggered
  //
  if (mReadyToLock == TRUE) {
    DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock event has been triggered! \n"));
    return EFI_ACCESS_DENIED;
  }

  Status = RegisterSerialIrqIfNull ();
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Insert a record
  //
  return InsertEspiRecord (DispatchFunction, PcNonFatalErr, DispatchHandle);
}

/**
  eSPI SMI Dispatch Protocol instance to register a Peripheral Channel 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
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 == TRUE) {
    DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock event has been triggered! \n"));
    return EFI_ACCESS_DENIED;
  }

  Status = RegisterSerialIrqIfNull ();
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Insert a record
  //
  return InsertEspiRecord (DispatchFunction, PcFatalErr, 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
  )
{
  EFI_STATUS  Status;

  //
  // Return access denied if the SmmReadyToLock event has been triggered
  //
  if (mReadyToLock == TRUE) {
    DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock event has been triggered! \n"));
    return EFI_ACCESS_DENIED;
  }

  Status = RegisterSerialIrqIfNull ();
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Insert a record
  //
  return InsertEspiRecord (DispatchFunction, VwNonFatalErr, DispatchHandle);
}

/**
  eSPI SMI Dispatch Protocol instance to register a Virtual Wire 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
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 == TRUE) {
    DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock event has been triggered! \n"));
    return EFI_ACCESS_DENIED;
  }

  Status = RegisterSerialIrqIfNull ();
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Insert a record
  //
  return InsertEspiRecord (DispatchFunction, VwFatalErr, DispatchHandle);
}

/**
  eSPI SMI Dispatch Protocol instance to register a Flash 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
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 == TRUE) {
    DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock event has been triggered! \n"));
    return EFI_ACCESS_DENIED;
  }

  Status = RegisterSerialIrqIfNull ();
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Insert a record
  //
  return InsertEspiRecord (DispatchFunction, FlashNonFatalErr, DispatchHandle);
}

/**
  eSPI SMI Dispatch Protocol instance to register a Flash Channel 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
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 == TRUE) {
    DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock event has been triggered! \n"));
    return EFI_ACCESS_DENIED;
  }

  Status = RegisterSerialIrqIfNull ();
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Insert a record
  //
  return InsertEspiRecord (DispatchFunction, FlashFatalErr, 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
  )
{
  EFI_STATUS Status;

  //
  // Return access denied if the SmmReadyToLock event has been triggered
  //
  if (mReadyToLock == TRUE) {
    DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock event has been triggered! \n"));
    return EFI_ACCESS_DENIED;
  }

  Status = RegisterSerialIrqIfNull ();
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Insert a record
  //
  return InsertEspiRecord (DispatchFunction, LnkType1Err, DispatchHandle);
}

//
// EspiSlave srcdesc
//
PCH_SMM_SOURCE_DESC mSrcDescEspiSlave = {
  PCH_SMM_NO_FLAGS,
  {
    {
      {
        ACPI_ADDR_TYPE,
        {R_PCH_SMI_EN}
      },
      S_PCH_SMI_EN,
      N_PCH_SMI_EN_ESPI
    },
    NULL_BIT_DESC_INITIALIZER
  },
  {
    {
      {
        ACPI_ADDR_TYPE,
        {R_PCH_SMI_STS}
      },
      S_PCH_SMI_STS,
      N_PCH_SMI_STS_ESPI
    }
  },
  {
    {
      ACPI_ADDR_TYPE,
      {R_PCH_SMI_STS}
    },
    S_PCH_SMI_STS,
    N_PCH_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 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 service the cause.
  After servicing it, it has to clear the Slaves' internal SMI# status registers

  @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     EFI_ACCESS_DENIED         The ESPI_SMI_LOCK is set and register 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;
  UINT32                                GenPmConA;
  UINTN                                 PmcBaseAddress;

  //
  // Return access denied if the SmmReadyToLock event has been triggered
  //
  if (mReadyToLock == TRUE) {
    DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock event has been triggered! \n"));
    return EFI_ACCESS_DENIED;
  }

  //
  // If ESPI_SMI_LOCK is set, the register is blocked.
  //
  PmcBaseAddress = MmPciBase (
                     DEFAULT_PCI_BUS_NUMBER_PCH,
                     PCI_DEVICE_NUMBER_PCH_PMC,
                     PCI_FUNCTION_NUMBER_PCH_PMC
                     );
  GenPmConA = MmioRead32 (PmcBaseAddress + R_PCH_PMC_GEN_PMCON_A);
  if (GenPmConA & B_PCH_PMC_GEN_PMCON_A_ESPI_SMI_LOCK) {
    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 DispatchFunction
  //        instead of EspiSmiCallback.
  //
  Status = PchSmiRecordInsert (
             &gPchEspiSmiDispatchProtocolGuid,
             &mSrcDescEspiSlave,
             (PCH_SMI_CALLBACK_FUNCTIONS) DispatchFunction,
             PchEspiSmiEspiSlaveType,
             DispatchHandle
             );
  PchSmmClearSource (&mSrcDescEspiSlave);
  PchSmmEnableSource (&mSrcDescEspiSlave);

  //
  // Lock down the ESPI_SMI_LOCK after ESPI SMI is enabled.
  //
  MmioOr8 (PmcBaseAddress + R_PCH_PMC_GEN_PMCON_A, B_PCH_PMC_GEN_PMCON_A_ESPI_SMI_LOCK);
  //
  // Keep the DispatchHandle which will be used for unregister function.
  //
  mEspiSmiInstance.PchSmiEspiHandle[EspiPmc] = *DispatchHandle;

  return Status;
}

/**
  eSPI SMI Dispatch Protocol instance to unregister a callback based on handle

  @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 bad pointer
  @retval     EFI_INVALID_PARAMETER   DispatchHandle does not exist in database
  @retval     EFI_ACCESS_DENIED       Unregistration is done after end of DXE
  @retval     EFI_ACCESS_DENIED       DispatchHandle is not allowed to unregistered
**/
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;

  if (DispatchHandle == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Return access denied if the SmmReadyToLock event has been triggered
  //
  if (mReadyToLock == TRUE) {
    DEBUG ((DEBUG_ERROR, "UnRegister is not allowed if the SmmReadyToLock event has been triggered! \n"));
    return EFI_ACCESS_DENIED;
  }

  if (((LIST_ENTRY *) DispatchHandle)->ForwardLink == (LIST_ENTRY *) EFI_BAD_POINTER) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // For DispatchHandle belongs to Espi Slave SMI, refuses the request of unregistration.
  //
  if (mEspiSmiInstance.PchSmiEspiHandle[EspiPmc] == DispatchHandle) {
    DEBUG ((DEBUG_ERROR, "UnRegister is not allowed for ESPI Slave SMI handle! \n"));
    return EFI_ACCESS_DENIED;
  }

  //
  // Iterate through all the database to find the record
  //
  for (EspiSmiType = 0; EspiSmiType < EspiSmiTypeMax; ++EspiSmiType) {
    LinkInDb = GetFirstNode (&mEspiSmiInstance.CallbackDataBase[EspiSmiType]);

    while (!IsNull (&mEspiSmiInstance.CallbackDataBase[EspiSmiType], LinkInDb)) {
      if (LinkInDb != (LIST_ENTRY *) DispatchHandle) {
        LinkInDb = GetNextNode (&mEspiSmiInstance.CallbackDataBase[EspiSmiType], LinkInDb);

      } else {
        //
        // Found the source to be from this list
        //
        RemoveEntryList (LinkInDb);
        RecordPointer = (ESPI_RECORD_FROM_LINK (LinkInDb));

        if (mEspiSmiInstance.EspiSmiEventCounter[EspiSmiType] != 0) {
          if (--mEspiSmiInstance.EspiSmiEventCounter[EspiSmiType] == 0) {
            EspiSmiDisableSource (EspiSmiType);
          }
        }

        SmiHandlerProfileUnregisterHandler (&gPchEspiSmiDispatchGuid, (EFI_SMM_HANDLER_ENTRY_POINT2)RecordPointer->Callback, &RecordPointer->EspiSmiType, sizeof(RecordPointer->EspiSmiType));

        Status = 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 = 0; EspiTopLevelType < EspiTopLevelTypeMax; ++EspiTopLevelType) {
    SafeToDisable = TRUE;
    //
    // Checks all the child events that belongs to a top level status in PMC
    //
    for (EspiSmiType = mEspiSmiInstance.Barrier[EspiTopLevelType].Start; EspiSmiType <= mEspiSmiInstance.Barrier[EspiTopLevelType].End; ++EspiSmiType) {
      if (mEspiSmiInstance.EspiSmiEventCounter[EspiSmiType] != 0) {
        SafeToDisable = FALSE;
      }
    }
    //
    // Finally, disable the top level event in PMC
    //
    if (SafeToDisable) {
      if (mEspiSmiInstance.PchSmiEspiHandle[EspiTopLevelType] != NULL) {
        Status = PchSmmCoreUnRegister (NULL, mEspiSmiInstance.PchSmiEspiHandle[EspiTopLevelType]);
        ASSERT_EFI_ERROR (Status);
        mEspiSmiInstance.PchSmiEspiHandle[EspiTopLevelType] = NULL;
      }
    }
  }

  return EFI_SUCCESS;
}
