/** @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 dispatchers
// Declared in PchSmm.h
//
GLOBAL_REMOVE_IF_UNREFERENCED UINT16 mAcpiBaseAddr;
GLOBAL_REMOVE_IF_UNREFERENCED UINT16 mTcoBaseAddr;
GLOBAL_REMOVE_IF_UNREFERENCED BOOLEAN mReadyToLock;
GLOBAL_REMOVE_IF_UNREFERENCED PRIVATE_DATA mPrivateData = {
{
NULL,
NULL
}, // CallbackDataBase linked list head
NULL, // EFI handle returned when calling InstallMultipleProtocolInterfaces
NULL, //
{ // protocol arrays
//
// elements within the array
//
{
PROTOCOL_SIGNATURE,
UsbType,
&gEfiSmmUsbDispatch2ProtocolGuid,
{{
(PCH_SMM_GENERIC_REGISTER) PchPiSmmCoreRegister,
(PCH_SMM_GENERIC_UNREGISTER) PchPiSmmCoreUnRegister
}}
},
{
PROTOCOL_SIGNATURE,
SxType,
&gEfiSmmSxDispatch2ProtocolGuid,
{{
(PCH_SMM_GENERIC_REGISTER) PchPiSmmCoreRegister,
(PCH_SMM_GENERIC_UNREGISTER) PchPiSmmCoreUnRegister
}}
},
{
PROTOCOL_SIGNATURE,
SwType,
&gEfiSmmSwDispatch2ProtocolGuid,
{{
(PCH_SMM_GENERIC_REGISTER) PchSwSmiRegister,
(PCH_SMM_GENERIC_UNREGISTER) PchSwSmiUnRegister,
(UINTN) MAXIMUM_SWI_VALUE
}}
},
{
PROTOCOL_SIGNATURE,
GpiType,
&gEfiSmmGpiDispatch2ProtocolGuid,
{{
(PCH_SMM_GENERIC_REGISTER) PchGpiSmiRegister,
(PCH_SMM_GENERIC_UNREGISTER) PchGpiSmiUnRegister,
(UINTN) PCH_GPIO_NUM_SUPPORTED_GPIS
}}
},
{
PROTOCOL_SIGNATURE,
PowerButtonType,
&gEfiSmmPowerButtonDispatch2ProtocolGuid,
{{
(PCH_SMM_GENERIC_REGISTER) PchPiSmmCoreRegister,
(PCH_SMM_GENERIC_UNREGISTER) PchPiSmmCoreUnRegister
}}
},
{
PROTOCOL_SIGNATURE,
PeriodicTimerType,
&gEfiSmmPeriodicTimerDispatch2ProtocolGuid,
{{
(PCH_SMM_GENERIC_REGISTER) PchPiSmmCoreRegister,
(PCH_SMM_GENERIC_UNREGISTER) PchPiSmmCoreUnRegister,
(UINTN) PchSmmPeriodicTimerDispatchGetNextShorterInterval
}}
},
}
};
GLOBAL_REMOVE_IF_UNREFERENCED CONTEXT_FUNCTIONS mContextFunctions[PCH_SMM_PROTOCOL_TYPE_MAX] = {
{
NULL,
NULL,
NULL
},
{
SxGetContext,
SxCmpContext,
NULL
},
{
NULL,
NULL,
NULL
},
{
NULL,
NULL,
NULL
},
{
PowerButtonGetContext,
PowerButtonCmpContext,
NULL
},
{
PeriodicTimerGetContext,
PeriodicTimerCmpContext,
PeriodicTimerGetCommBuffer
},
};
//
// PROTOTYPES
//
// Functions use only in this file
//
EFI_STATUS
EFIAPI
PchSmmCoreDispatcher (
IN EFI_HANDLE SmmImageHandle,
IN CONST VOID *PchSmmCore, OPTIONAL
IN OUT VOID *CommunicationBuffer,
IN OUT UINTN *SourceSize
);
//
// FUNCTIONS
//
/**
SMM ready to lock notification event handler.
@param Protocol Points to the protocol's unique identifier
@param Interface Points to the interface instance
@param Handle The handle on which the interface was installed
@retval EFI_SUCCESS SmmReadyToLockCallback runs successfully
**/
EFI_STATUS
EFIAPI
SmmReadyToLockCallback (
IN CONST EFI_GUID *Protocol,
IN VOID *Interface,
IN EFI_HANDLE Handle
)
{
mReadyToLock = TRUE;
return EFI_SUCCESS;
}
/**
PchSmiDispatcher SMM Module Entry Point\n
- Introduction\n
The PchSmiDispatcher module is an SMM driver which provides SMI handler registration
services for PCH generated SMIs.
- Details\n
This module provides SMI handler registration servicies for PCH SMIs.
NOTE: All the register/unregister functions will be locked after SMM ready to boot signal event.
Please make sure no handler is installed after that.
- @pre
- EFI_SMM_BASE2_PROTOCOL
- Documented in the System Management Mode Core Interface Specification
- EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL
- Documented in the UEFI 2.0 Specification and above
- EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL
- This is to ensure that PCI MMIO and IO resource has been prepared and available for this driver to allocate.
- EFI_SMM_CPU_PROTOCOL
- @result
The PchSmiDispatcher driver produces:
- EFI_SMM_USB_DISPATCH2_PROTOCOL
- EFI_SMM_SX_DISPATCH2_PROTOCOL
- EFI_SMM_SW_DISPATCH2_PROTOCOL
- EFI_SMM_GPI_DISPATCH2_PROTOCOL
- EFI_SMM_IO_TRAP_DISPATCH2_PROTOCOL
- EFI_SMM_POWER_BUTTON_DISPATCH2_PROTOCOL
- EFI_SMM_PERIODIC_TIMER_DISPATCH2_PROTOCOL
- @link _PCH_SMM_IO_TRAP_CONTROL_PROTOCOL PCH_SMM_IO_TRAP_CONTROL_PROTOCOL @endlink
- @link _PCH_PCIE_SMI_DISPATCH_PROTOCOL PCH_PCIE_SMI_DISPATCH_PROTOCOL @endlink
- @link _PCH_TCO_SMI_DISPATCH_PROTOCOL PCH_TCO_SMI_DISPATCH_PROTOCOL @endlink
- @link _PCH_ACPI_SMI_DISPATCH_PROTOCOL PCH_ACPI_SMI_DISPATCH_PROTOCOL @endlink
- @link _PCH_SMI_DISPATCH_PROTOCOL PCH_SMI_DISPATCH_PROTOCOL @endlink
- @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 completed.
**/
EFI_STATUS
EFIAPI
InitializePchSmmDispatcher (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
VOID *SmmReadyToLockRegistration;
//
// Access ACPI Base Addresses Register
//
mAcpiBaseAddr = PmcGetAcpiBase ();
ASSERT (mAcpiBaseAddr != 0);
//
// Access TCO Base Addresses Register
//
PchTcoBaseGet (&mTcoBaseAddr);
ASSERT (mTcoBaseAddr != 0);
//
// Register a callback function to handle subsequent SMIs. This callback
// will be called by SmmCoreDispatcher.
//
Status = gSmst->SmiHandlerRegister (PchSmmCoreDispatcher, NULL, &mPrivateData.SmiHandle);
ASSERT_EFI_ERROR (Status);
//
// Initialize Callback DataBase
//
InitializeListHead (&mPrivateData.CallbackDataBase);
//
// Enable SMIs on the PCH now that we have a callback
//
PchSmmInitHardware ();
//
// Install and initialize all the needed protocols
//
PchSwDispatchInit ();
PchSmmPublishDispatchProtocols ();
InstallPchSmiDispatchProtocols ();
InstallIoTrap (ImageHandle);
InstallEspiSmi (ImageHandle);
InstallPchSmmPeriodicTimerControlProtocol (mPrivateData.InstallMultProtHandle);
//
// Register EFI_SMM_READY_TO_LOCK_PROTOCOL_GUID notify function.
//
Status = gSmst->SmmRegisterProtocolNotify (
&gEfiSmmReadyToLockProtocolGuid,
SmmReadyToLockCallback,
&SmmReadyToLockRegistration
);
ASSERT_EFI_ERROR (Status);
return EFI_SUCCESS;
}
/**
The internal function used to create and insert a database record
@param[in] InsertRecord Record to insert to database.
@param[out] DispatchHandle Handle of dispatch function to register.
@retval EFI_INVALID_PARAMETER Error with NULL SMI source description
@retval EFI_OUT_OF_RESOURCES Fail to allocate pool for database record
@retval EFI_SUCCESS The database record is created successfully.
**/
EFI_STATUS
SmmCoreInsertRecord (
IN DATABASE_RECORD *NewRecord,
OUT EFI_HANDLE *DispatchHandle
)
{
EFI_STATUS Status;
DATABASE_RECORD *Record;
if ((NewRecord == NULL) ||
(NewRecord->Signature != DATABASE_RECORD_SIGNATURE))
{
ASSERT (FALSE);
return EFI_INVALID_PARAMETER;
}
Status = gSmst->SmmAllocatePool (EfiRuntimeServicesData, sizeof (DATABASE_RECORD), (VOID **) &Record);
if (EFI_ERROR (Status)) {
ASSERT (FALSE);
return EFI_OUT_OF_RESOURCES;
}
CopyMem (Record, NewRecord, sizeof (DATABASE_RECORD));
//
// After ensuring the source of event is not null, we will insert the record into the database
//
InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link);
//
// Child's handle will be the address linked list link in the record
//
*DispatchHandle = (EFI_HANDLE) (&Record->Link);
return EFI_SUCCESS;
}
/**
Unregister a child SMI source dispatch function with a parent SMM driver
@param[in] This Protocol instance pointer.
@param[in] DispatchHandle Handle of dispatch function to deregister.
@retval EFI_SUCCESS The dispatch function has been successfully
unregistered and the SMI source has been disabled
if there are no other registered child dispatch
functions for this SMI source.
@retval EFI_INVALID_PARAMETER Handle is invalid.
@retval EFI_ACCESS_DENIED Return access denied if the SmmReadyToLock event has been triggered
**/
EFI_STATUS
EFIAPI
PchPiSmmCoreUnRegister (
IN PCH_SMM_GENERIC_PROTOCOL *This,
IN EFI_HANDLE *DispatchHandle
)
{
DATABASE_RECORD *RecordToDelete;
EFI_STATUS Status;
PCH_SMM_QUALIFIED_PROTOCOL *Qualified;
Qualified = QUALIFIED_PROTOCOL_FROM_GENERIC (This);
RecordToDelete = DATABASE_RECORD_FROM_LINK (DispatchHandle);
Status = PchSmmCoreUnRegister (NULL, DispatchHandle);
if (!EFI_ERROR (Status)) {
SmiHandlerProfileUnregisterHandler (Qualified->Guid, RecordToDelete->Callback, 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 invoked for this SMI source.
@param[in] DispatchContext Pointer to the dispatch function's context.
@param[out] DispatchHandle Handle of dispatch function, for when interfacing
with the parent SMM driver, will be the address of linked
list link in the call back record.
@retval EFI_OUT_OF_RESOURCES Insufficient resources to create database record
@retval EFI_INVALID_PARAMETER The input parameter is invalid
@retval EFI_SUCCESS The dispatch function has been successfully
registered and the SMI source has been enabled.
**/
EFI_STATUS
EFIAPI
PchPiSmmCoreRegister (
IN PCH_SMM_GENERIC_PROTOCOL *This,
IN EFI_SMM_HANDLER_ENTRY_POINT2 DispatchFunction,
IN PCH_SMM_CONTEXT *DispatchContext,
OUT EFI_HANDLE *DispatchHandle
)
{
EFI_STATUS Status;
DATABASE_RECORD Record;
PCH_SMM_QUALIFIED_PROTOCOL *Qualified;
PCH_SMM_SOURCE_DESC NullSourceDesc;
//
// Initialize NullSourceDesc
//
NullInitSourceDesc (&NullSourceDesc);
//
// Return access denied if the SmmReadyToLock event has been triggered
//
if (mReadyToLock == TRUE) {
DEBUG ((DEBUG_ERROR, "Register is not allowed if the EndOfDxe event has been triggered! \n"));
return EFI_ACCESS_DENIED;
}
ZeroMem (&Record, sizeof (DATABASE_RECORD));
//
// Gather information about the registration request
//
Record.Callback = DispatchFunction;
Qualified = QUALIFIED_PROTOCOL_FROM_GENERIC (This);
Record.ProtocolType = Qualified->Type;
Record.ContextFunctions = mContextFunctions[Qualified->Type];
//
// Perform linked list housekeeping
//
Record.Signature = DATABASE_RECORD_SIGNATURE;
switch (Qualified->Type) {
//
// By the end of this switch statement, we'll know the
// source description the child is registering for
//
case UsbType:
Record.ContextSize = sizeof (EFI_SMM_USB_REGISTER_CONTEXT);
CopyMem (&Record.ChildContext, DispatchContext, Record.ContextSize);
//
// Check the validity of Context Type
//
if ((Record.ChildContext.Usb.Type < UsbLegacy) || (Record.ChildContext.Usb.Type > UsbWake)) {
return EFI_INVALID_PARAMETER;
}
MapUsbToSrcDesc (DispatchContext, &Record.SrcDesc);
Record.ClearSource = NULL;
//
// use default clear source function
//
break;
case SxType:
Record.ContextSize = sizeof (EFI_SMM_SX_REGISTER_CONTEXT);
CopyMem (&Record.ChildContext, DispatchContext, Record.ContextSize);
//
// Check the validity of Context Type and Phase
//
if ((Record.ChildContext.Sx.Type < SxS0) ||
(Record.ChildContext.Sx.Type >= EfiMaximumSleepType) ||
(Record.ChildContext.Sx.Phase < SxEntry) ||
(Record.ChildContext.Sx.Phase >= EfiMaximumPhase)
) {
return EFI_INVALID_PARAMETER;
}
CopyMem (&Record.SrcDesc, &mSxSourceDesc, sizeof (PCH_SMM_SOURCE_DESC));
Record.ClearSource = NULL;
//
// use default clear source function
//
break;
case PowerButtonType:
Record.ContextSize = sizeof (EFI_SMM_POWER_BUTTON_REGISTER_CONTEXT);
CopyMem (&Record.ChildContext, DispatchContext, Record.ContextSize);
//
// Check the validity of Context Phase
//
if ((Record.ChildContext.PowerButton.Phase < EfiPowerButtonEntry) ||
(Record.ChildContext.PowerButton.Phase > EfiPowerButtonExit))
{
return EFI_INVALID_PARAMETER;
}
CopyMem (&Record.SrcDesc, &mPowerButtonSourceDesc, sizeof (PCH_SMM_SOURCE_DESC));
Record.ClearSource = NULL;
//
// use default clear source function
//
break;
case PeriodicTimerType:
Record.ContextSize = sizeof (EFI_SMM_PERIODIC_TIMER_REGISTER_CONTEXT);
CopyMem (&Record.ChildContext, DispatchContext, Record.ContextSize);
//
// Check the validity of timer value
//
if (DispatchContext->PeriodicTimer.SmiTickInterval <= 0) {
return EFI_INVALID_PARAMETER;
}
MapPeriodicTimerToSrcDesc (DispatchContext, &Record.SrcDesc);
Record.ClearSource = PchSmmPeriodicTimerClearSource;
break;
default:
return EFI_INVALID_PARAMETER;
break;
}
if (CompareSources (&Record.SrcDesc, &NullSourceDesc)) {
return EFI_INVALID_PARAMETER;
}
//
// After ensuring the source of event is not null, we will insert the record into the database
// Child's handle will be the address linked list link in the record
//
Status = SmmCoreInsertRecord (
&Record,
DispatchHandle
);
ASSERT_EFI_ERROR (Status);
if (Record.ClearSource == NULL) {
//
// Clear the SMI associated w/ the source using the default function
//
PchSmmClearSource (&Record.SrcDesc);
} else {
//
// This source requires special handling to clear
//
Record.ClearSource (&Record.SrcDesc);
}
PchSmmEnableSource (&Record.SrcDesc);
SmiHandlerProfileRegisterHandler (Qualified->Guid, DispatchFunction, (UINTN)RETURN_ADDRESS (0), DispatchContext, Record.ContextSize);
return EFI_SUCCESS;
}
/**
Unregister a child SMI source dispatch function with a parent SMM driver.
@param[in] This Pointer to the PCH_SMM_GENERIC_PROTOCOL instance.
@param[in] DispatchHandle Handle of dispatch function to deregister.
@retval EFI_SUCCESS The dispatch function has been successfully
unregistered and the SMI source has been disabled
if there are no other registered child dispatch
functions for this SMI source.
@retval EFI_INVALID_PARAMETER Handle is invalid.
**/
EFI_STATUS
EFIAPI
PchSmmCoreUnRegister (
IN PCH_SMM_GENERIC_PROTOCOL *This,
IN EFI_HANDLE *DispatchHandle
)
{
EFI_STATUS Status;
BOOLEAN NeedClearEnable;
UINTN DescIndex;
DATABASE_RECORD *RecordToDelete;
DATABASE_RECORD *RecordInDb;
LIST_ENTRY *LinkInDb;
if (DispatchHandle == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Return access denied if the SmmReadyToLock event has been triggered
//
if (mReadyToLock == TRUE) {
DEBUG ((DEBUG_ERROR, "UnRegister is not allowed if the SmmReadyToLock event has been triggered! \n"));
return EFI_ACCESS_DENIED;
}
RecordToDelete = DATABASE_RECORD_FROM_LINK (DispatchHandle);
//
// Take the entry out of the linked list
//
if (RecordToDelete->Link.ForwardLink == (LIST_ENTRY *) EFI_BAD_POINTER) {
return EFI_INVALID_PARAMETER;
}
RemoveEntryList (&RecordToDelete->Link);
//
// Loop through all the souces in record linked list to see if any source enable is equal.
// If any source enable is equal, we do not want to disable it.
//
for (DescIndex = 0; DescIndex < NUM_EN_BITS; ++DescIndex) {
if (IS_BIT_DESC_NULL (RecordToDelete->SrcDesc.En[DescIndex])) {
continue;
}
NeedClearEnable = TRUE;
LinkInDb = GetFirstNode (&mPrivateData.CallbackDataBase);
while (!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) {
RecordInDb = DATABASE_RECORD_FROM_LINK (LinkInDb);
if (IsBitEqualToAnySourceEn (&RecordToDelete->SrcDesc.En[DescIndex], &RecordInDb->SrcDesc)) {
NeedClearEnable = FALSE;
break;
}
LinkInDb = GetNextNode (&mPrivateData.CallbackDataBase, &RecordInDb->Link);
}
if (NeedClearEnable == FALSE) {
continue;
}
WriteBitDesc (&RecordToDelete->SrcDesc.En[DescIndex], 0, FALSE);
}
Status = gSmst->SmmFreePool (RecordToDelete);
if (EFI_ERROR (Status)) {
ASSERT (FALSE);
return Status;
}
return EFI_SUCCESS;
}
/**
This function clears the pending SMI status before set EOS.
NOTE: This only clears the pending SMI with known reason.
Please do not clear unknown pending SMI status since that will hide potential issues.
@param[in] SmiStsValue SMI status
@param[in] SciEn Sci Enable status
**/
STATIC
VOID
ClearPendingSmiStatus (
UINT32 SmiStsValue,
BOOLEAN SciEn
)
{
//
// Clear NewCentury status if it's not handled.
//
if (SmiStsValue & B_ACPI_IO_SMI_STS_TCO) {
if (IoRead16 (mTcoBaseAddr + R_TCO_IO_TCO1_STS) & B_TCO_IO_TCO1_STS_NEWCENTURY) {
PchTcoSmiClearSourceAndBlock (&mSrcDescNewCentury);
}
}
// Clear PWRBTNOR_STS if it's not handled.
//
if (IoRead16 (mAcpiBaseAddr + R_ACPI_IO_PM1_STS) & B_ACPI_IO_PM1_STS_PRBTNOR) {
IoWrite16 (mAcpiBaseAddr + R_ACPI_IO_PM1_STS, B_ACPI_IO_PM1_STS_PRBTNOR);
}
//
// Clear WADT_STS if this is triggered by WADT timer.
//
if (!SciEn) {
if ((IoRead32 (mAcpiBaseAddr + R_ACPI_IO_GPE0_EN_127_96) & B_ACPI_IO_GPE0_EN_127_96_WADT) &&
(IoRead32 (mAcpiBaseAddr + R_ACPI_IO_GPE0_STS_127_96) & B_ACPI_IO_GPE0_STS_127_96_WADT)) {
IoWrite32 (mAcpiBaseAddr + R_ACPI_IO_GPE0_STS_127_96, B_ACPI_IO_GPE0_STS_127_96_WADT);
}
}
//
// Clear GPIO_UNLOCK_SMI_STS in case it is set as GPIO Unlock SMI is not supported
//
if (SmiStsValue & B_ACPI_IO_SMI_STS_GPIO_UNLOCK) {
IoWrite32 (mAcpiBaseAddr + R_ACPI_IO_SMI_STS, B_ACPI_IO_SMI_STS_GPIO_UNLOCK);
}
}
/**
The callback function to handle subsequent SMIs. This callback will be called by SmmCoreDispatcher.
@param[in] SmmImageHandle Not used
@param[in] PchSmmCore Not used
@param[in, out] CommunicationBuffer Not used
@param[in, out] SourceSize Not used
@retval EFI_SUCCESS Function successfully completed
**/
EFI_STATUS
EFIAPI
PchSmmCoreDispatcher (
IN EFI_HANDLE SmmImageHandle,
IN CONST VOID *PchSmmCore,
IN OUT VOID *CommunicationBuffer,
IN OUT UINTN *SourceSize
)
{
//
// Used to prevent infinite loops
//
UINTN EscapeCount;
BOOLEAN ContextsMatch;
BOOLEAN EosSet;
BOOLEAN SxChildWasDispatched;
DATABASE_RECORD *RecordInDb;
LIST_ENTRY *LinkInDb;
DATABASE_RECORD *RecordToExhaust;
LIST_ENTRY *LinkToExhaust;
PCH_SMM_CONTEXT Context;
VOID *CommBuffer;
UINTN CommBufferSize;
EFI_STATUS Status;
BOOLEAN SciEn;
UINT32 SmiEnValue;
UINT32 SmiStsValue;
UINT8 Port74Save;
UINT8 Port76Save;
PCH_SMM_SOURCE_DESC ActiveSource;
//
// Initialize ActiveSource
//
NullInitSourceDesc (&ActiveSource);
EscapeCount = 3;
ContextsMatch = FALSE;
EosSet = FALSE;
SxChildWasDispatched = FALSE;
Status = EFI_SUCCESS;
//
// Save IO index registers
// @note: Save/Restore port 70h directly might break NMI_EN# setting,
// then save/restore 74h/76h instead.
// @note: CF8 is not saved. Prefer method is to use MMIO instead of CF8
//
Port76Save = IoRead8 (R_RTC_IO_EXT_INDEX_ALT);
Port74Save = IoRead8 (R_RTC_IO_INDEX_ALT);
if (!IsListEmpty (&mPrivateData.CallbackDataBase)) {
//
// We have children registered w/ us -- continue
//
while ((!EosSet) && (EscapeCount > 0)) {
EscapeCount--;
LinkInDb = GetFirstNode (&mPrivateData.CallbackDataBase);
//
// Cache SciEn, SmiEnValue and SmiStsValue to determine if source is active
//
SciEn = PchSmmGetSciEn ();
SmiEnValue = IoRead32 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_SMI_EN));
SmiStsValue = IoRead32 ((UINTN) (mAcpiBaseAddr + R_ACPI_IO_SMI_STS));
while (!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) {
RecordInDb = DATABASE_RECORD_FROM_LINK (LinkInDb);
//
// look for the first active source
//
if (!SourceIsActive (&RecordInDb->SrcDesc, SciEn, SmiEnValue, SmiStsValue)) {
//
// Didn't find the source yet, keep looking
//
LinkInDb = GetNextNode (&mPrivateData.CallbackDataBase, &RecordInDb->Link);
//
// if it's the last one, try to clear EOS
//
if (IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) {
//
// Clear pending SMI status before EOS
//
ClearPendingSmiStatus (SmiStsValue, SciEn);
EosSet = PchSmmSetAndCheckEos ();
}
} else {
//
// We found a source. If this is a sleep type, we have to go to
// appropriate sleep state anyway.No matter there is sleep child or not
//
if (RecordInDb->ProtocolType == SxType) {
SxChildWasDispatched = TRUE;
}
//
// "cache" the source description and don't query I/O anymore
//
CopyMem ((VOID *) &ActiveSource, (VOID *) &(RecordInDb->SrcDesc), sizeof (PCH_SMM_SOURCE_DESC));
LinkToExhaust = LinkInDb;
//
// exhaust the rest of the queue looking for the same source
//
while (!IsNull (&mPrivateData.CallbackDataBase, LinkToExhaust)) {
RecordToExhaust = DATABASE_RECORD_FROM_LINK (LinkToExhaust);
//
// RecordToExhaust->Link might be removed (unregistered) by Callback function, and then the
// system will hang in ASSERT() while calling GetNextNode().
// To prevent the issue, we need to get next record in DB here (before Callback function).
//
LinkToExhaust = GetNextNode (&mPrivateData.CallbackDataBase, &RecordToExhaust->Link);
if (CompareSources (&RecordToExhaust->SrcDesc, &ActiveSource)) {
//
// These source descriptions are equal, so this callback should be
// dispatched.
//
if (RecordToExhaust->ContextFunctions.GetContext != NULL) {
//
// This child requires that we get a calling context from
// hardware and compare that context to the one supplied
// by the child.
//
ASSERT (RecordToExhaust->ContextFunctions.CmpContext != NULL);
//
// Make sure contexts match before dispatching event to child
//
RecordToExhaust->ContextFunctions.GetContext (RecordToExhaust, &Context);
ContextsMatch = RecordToExhaust->ContextFunctions.CmpContext (&Context, &RecordToExhaust->ChildContext);
} else {
//
// This child doesn't require any more calling context beyond what
// it supplied in registration. Simply pass back what it gave us.
//
Context = RecordToExhaust->ChildContext;
ContextsMatch = TRUE;
}
if (ContextsMatch) {
if (RecordToExhaust->ProtocolType == PchSmiDispatchType) {
//
// For PCH SMI dispatch protocols
//
PchSmiTypeCallbackDispatcher (RecordToExhaust);
} else {
//
// For EFI standard SMI dispatch protocols
//
if (RecordToExhaust->Callback != NULL) {
if (RecordToExhaust->ContextFunctions.GetCommBuffer != NULL) {
//
// This callback function needs CommBuffer and CommBufferSize.
// Get those from child and then pass to callback function.
//
RecordToExhaust->ContextFunctions.GetCommBuffer (RecordToExhaust, &CommBuffer, &CommBufferSize);
} else {
//
// Child doesn't support the CommBuffer and CommBufferSize.
// Just pass NULL value to callback function.
//
CommBuffer = NULL;
CommBufferSize = 0;
}
PERF_START_EX (NULL, "SmmFunction", NULL, AsmReadTsc (), RecordToExhaust->ProtocolType);
RecordToExhaust->Callback ((EFI_HANDLE) & RecordToExhaust->Link, &Context, CommBuffer, &CommBufferSize);
PERF_END_EX (NULL, "SmmFunction", NULL, AsmReadTsc (), RecordToExhaust->ProtocolType);
if (RecordToExhaust->ProtocolType == SxType) {
SxChildWasDispatched = TRUE;
}
} else {
ASSERT (FALSE);
}
}
}
}
}
if (RecordInDb->ClearSource == NULL) {
//
// Clear the SMI associated w/ the source using the default function
//
PchSmmClearSource (&ActiveSource);
} else {
//
// This source requires special handling to clear
//
RecordInDb->ClearSource (&ActiveSource);
}
//
// Clear pending SMI status before EOS
//
ClearPendingSmiStatus (SmiStsValue, SciEn);
//
// Also, try to clear EOS
//
EosSet = PchSmmSetAndCheckEos ();
//
// Queue is empty, reset the search
//
break;
}
}
}
}
//
// 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 received prior
// to this driver.
// If there is an asynchronous SMI that occurs while processing the Callback, let
// all of the drivers (including this one) have an opportunity to scan for 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 generates 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 call;
// 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;
}