/** @file
This driver is responsible for the registration of child drivers
and the abstraction of the PCH SMI sources.
Copyright (c) 2017 - 2019, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "PchSmm.h"
#include "PchSmmHelpers.h"
#include "PchSmmEspi.h"
//
// 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) PchSmmCoreRegister,
(PCH_SMM_GENERIC_UNREGISTER) PchSmmCoreUnRegister,
}}
},
{
PROTOCOL_SIGNATURE,
SxType,
&gEfiSmmSxDispatch2ProtocolGuid,
{{
(PCH_SMM_GENERIC_REGISTER) PchSmmCoreRegister,
(PCH_SMM_GENERIC_UNREGISTER) PchSmmCoreUnRegister
}}
},
{
PROTOCOL_SIGNATURE,
SwType,
&gEfiSmmSwDispatch2ProtocolGuid,
{{
(PCH_SMM_GENERIC_REGISTER) PchSmmCoreRegister,
(PCH_SMM_GENERIC_UNREGISTER) PchSmmCoreUnRegister,
(UINTN) MAXIMUM_SWI_VALUE
}}
},
{
PROTOCOL_SIGNATURE,
GpiType,
&gEfiSmmGpiDispatch2ProtocolGuid,
{{
(PCH_SMM_GENERIC_REGISTER) PchSmmCoreRegister,
(PCH_SMM_GENERIC_UNREGISTER) PchSmmCoreUnRegister,
(UINTN) V_PCH_GPIO_NUM_SUPPORTED_GPIS
}}
},
{
PROTOCOL_SIGNATURE,
PowerButtonType,
&gEfiSmmPowerButtonDispatch2ProtocolGuid,
{{
(PCH_SMM_GENERIC_REGISTER) PchSmmCoreRegister,
(PCH_SMM_GENERIC_UNREGISTER) PchSmmCoreUnRegister
}}
},
{
PROTOCOL_SIGNATURE,
PeriodicTimerType,
&gEfiSmmPeriodicTimerDispatch2ProtocolGuid,
{{
(PCH_SMM_GENERIC_REGISTER) PchSmmCoreRegister,
(PCH_SMM_GENERIC_UNREGISTER) PchSmmCoreUnRegister,
(UINTN) PchSmmPeriodicTimerDispatchGetNextShorterInterval
}}
},
}
};
GLOBAL_REMOVE_IF_UNREFERENCED CONTEXT_FUNCTIONS mContextFunctions[PCH_SMM_PROTOCOL_TYPE_MAX] = {
{
NULL,
NULL,
NULL
},
{
SxGetContext,
SxCmpContext,
NULL
},
{
SwGetContext,
SwCmpContext,
SwGetCommBuffer
},
{
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_GPIO_UNLOCK_SMI_DISPATCH_PROTOCOL PCH_GPIO_UNLOCK_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
)
{
UINTN LpcBaseAddress;
EFI_STATUS Status;
VOID *SmmReadyToLockRegistration;
///
/// Access ACPI Base Addresses Register
///
LpcBaseAddress = MmPciBase (
DEFAULT_PCI_BUS_NUMBER_PCH,
PCI_DEVICE_NUMBER_PCH_LPC,
PCI_FUNCTION_NUMBER_PCH_LPC
);
PchAcpiBaseGet (&mAcpiBaseAddr);
ASSERT (mAcpiBaseAddr != 0);
///
/// Access TCO Base Addresses Register
///
PchTcoBaseGet (&mTcoBaseAddr);
ASSERT (mTcoBaseAddr != 0);
PchSwDispatchInit ();
///
/// 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
//
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;
}
/**
Check the Fed SwSmiInputValue to see if there is a duplicated one in the database
@param[in] FedSwSmiInputValue Fed SwSmiInputValue
@retval EFI_SUCCESS There is no duplicated SwSmiInputValue
@retval EFI_INVALID_PARAMETER There is a duplicated SwSmiInputValue
**/
EFI_STATUS
SmiInputValueDuplicateCheck (
UINTN FedSwSmiInputValue
)
{
DATABASE_RECORD *RecordInDb;
LIST_ENTRY *LinkInDb;
LinkInDb = GetFirstNode (&mPrivateData.CallbackDataBase);
while (!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) {
RecordInDb = DATABASE_RECORD_FROM_LINK (LinkInDb);
if (RecordInDb->ProtocolType == SwType) {
if (RecordInDb->ChildContext.Sw.SwSmiInputValue == FedSwSmiInputValue) {
return EFI_INVALID_PARAMETER;
}
}
LinkInDb = GetNextNode (&mPrivateData.CallbackDataBase, &RecordInDb->Link);
}
return EFI_SUCCESS;
}
/**
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
PchSmmCoreRegister (
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;
UINTN Index;
GPIO_PAD GpioPad;
UINT8 GpiSmiBitOffset;
UINT32 GpiSmiEnRegAddress;
UINT32 GpiSmiStsRegAddress;
UINT32 Data32Or;
UINT32 Data32And;
UINTN ContextSize;
//
// 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;
}
Index = 0;
///
/// Create database record and add to database
///
Status = gSmst->SmmAllocatePool (EfiRuntimeServicesData, sizeof (DATABASE_RECORD), (VOID **) &Record);
if (EFI_ERROR (Status)) {
ASSERT (FALSE);
return EFI_OUT_OF_RESOURCES;
}
ZeroMem (Record, sizeof (DATABASE_RECORD));
///
/// Gather information about the registration request
///
Record->Callback = DispatchFunction;
Record->ChildContext = *DispatchContext;
Qualified = QUALIFIED_PROTOCOL_FROM_GENERIC (This);
Record->ProtocolType = Qualified->Type;
Record->ContextFunctions = mContextFunctions[Qualified->Type];
///
/// Perform linked list housekeeping
///
Record->Signature = DATABASE_RECORD_SIGNATURE;
ContextSize = 0;
switch (Qualified->Type) {
///
/// By the end of this switch statement, we'll know the
/// source description the child is registering for
///
case UsbType:
CopyMem (&Record->ChildContext, DispatchContext, sizeof (EFI_SMM_USB_REGISTER_CONTEXT));
///
/// Check the validity of Context Type
///
if ((Record->ChildContext.Usb.Type < UsbLegacy) || (Record->ChildContext.Usb.Type > UsbWake)) {
goto Error;
}
MapUsbToSrcDesc (DispatchContext, &(Record->SrcDesc));
Record->ClearSource = NULL;
ContextSize = sizeof(Record->ChildContext.Usb);
///
/// use default clear source function
///
break;
case SxType:
CopyMem (&Record->ChildContext, DispatchContext, sizeof (EFI_SMM_SX_REGISTER_CONTEXT));
///
/// 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)
) {
goto Error;
}
CopyMem ((VOID *) &(Record->SrcDesc), (VOID *) (&SX_SOURCE_DESC), sizeof (PCH_SMM_SOURCE_DESC));
Record->ClearSource = NULL;
ContextSize = sizeof(Record->ChildContext.Sx);
///
/// use default clear source function
///
break;
case SwType:
CopyMem (&Record->ChildContext, DispatchContext, sizeof (EFI_SMM_SW_REGISTER_CONTEXT));
///
/// Check the validity of Context Value
///
if (Record->ChildContext.Sw.SwSmiInputValue == (UINTN) -1) {
for (Index = 1; Index < MAXIMUM_SWI_VALUE; Index++) {
if (!EFI_ERROR (SmiInputValueDuplicateCheck (Index))) {
Record->ChildContext.Sw.SwSmiInputValue = Index;
break;
}
}
if (Record->ChildContext.Sw.SwSmiInputValue == (UINTN) -1) {
goto Error;
}
}
if (Record->ChildContext.Sw.SwSmiInputValue > MAXIMUM_SWI_VALUE) {
goto Error;
}
if (EFI_ERROR (SmiInputValueDuplicateCheck (Record->ChildContext.Sw.SwSmiInputValue))) {
goto Error;
}
CopyMem (DispatchContext, &Record->ChildContext, sizeof (EFI_SMM_SW_REGISTER_CONTEXT));
CopyMem ((VOID *) &(Record->SrcDesc), (VOID *) (&SW_SOURCE_DESC), sizeof (PCH_SMM_SOURCE_DESC));
Record->ClearSource = NULL;
ContextSize = sizeof(Record->ChildContext.Sw);
///
/// use default clear source function
///
break;
case GpiType:
CopyMem (&Record->ChildContext, DispatchContext, sizeof (EFI_SMM_GPI_REGISTER_CONTEXT));
Index = Record->ChildContext.Gpi.GpiNum;
Status = GpioGetPadAndSmiRegs (
(UINT32) Index,
&GpioPad,
&GpiSmiBitOffset,
&GpiSmiEnRegAddress,
&GpiSmiStsRegAddress
);
if (EFI_ERROR (Status)) {
goto Error;
}
CopyMem (
(VOID *) &(Record->SrcDesc),
(VOID *) &(PCH_GPI_SOURCE_DESC_TEMPLATE),
sizeof (PCH_SMM_SOURCE_DESC)
);
Record->SrcDesc.En[0].Reg.Data.raw = GpiSmiEnRegAddress; // GPI SMI Enable register
Record->SrcDesc.En[0].Bit = GpiSmiBitOffset; // Bit position for selected pad
Record->SrcDesc.Sts[0].Reg.Data.raw = GpiSmiStsRegAddress; // GPI SMI Status register
Record->SrcDesc.Sts[0].Bit = GpiSmiBitOffset; // Bit position for selected pad
Record->ClearSource = NULL;
ContextSize = sizeof(Record->ChildContext.Gpi);
///
/// use default clear source function
///
///
/// Add GPI_SMI_EN programming into S3 boot script
///
Data32Or = (1u << GpiSmiBitOffset);
Data32And = 0xFFFFFFFF;
S3BootScriptSaveMemReadWrite (S3BootScriptWidthUint32, GpiSmiEnRegAddress, &Data32Or, &Data32And);
break;
case PowerButtonType:
CopyMem (&Record->ChildContext, DispatchContext, sizeof (EFI_SMM_POWER_BUTTON_REGISTER_CONTEXT));
///
/// Check the validity of Context Phase
///
if ((Record->ChildContext.PowerButton.Phase < EfiPowerButtonEntry) ||
(Record->ChildContext.PowerButton.Phase > EfiPowerButtonExit)
) {
goto Error;
}
CopyMem ((VOID *) &(Record->SrcDesc), (VOID *) &POWER_BUTTON_SOURCE_DESC, sizeof (PCH_SMM_SOURCE_DESC));
Record->ClearSource = NULL;
ContextSize = sizeof(Record->ChildContext.PowerButton);
///
/// use default clear source function
///
break;
case PeriodicTimerType:
CopyMem (&Record->ChildContext, DispatchContext, sizeof (EFI_SMM_PERIODIC_TIMER_REGISTER_CONTEXT));
///
/// Check the validity of timer value
///
if (DispatchContext->PeriodicTimer.SmiTickInterval <= 0) {
goto Error;
}
MapPeriodicTimerToSrcDesc (DispatchContext, &(Record->SrcDesc));
Record->ClearSource = PchSmmPeriodicTimerClearSource;
ContextSize = sizeof(Record->ChildContext.PeriodicTimer);
break;
default:
goto Error;
break;
}
if (CompareSources (&Record->SrcDesc, &NullSourceDesc)) {
goto Error;
}
///
/// After ensuring the source of event is not null, we will insert the record into the database
///
InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link);
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);
///
/// Child's handle will be the address linked list link in the record
///
*DispatchHandle = (EFI_HANDLE) (&Record->Link);
*DispatchContext = Record->ChildContext;
SmiHandlerProfileRegisterHandler (Qualified->Guid, DispatchFunction, (UINTN)RETURN_ADDRESS (0), &Record->ChildContext, ContextSize);
return EFI_SUCCESS;
Error:
Status = gSmst->SmmFreePool (Record);
///
/// DEBUG((DEBUG_ERROR,"Free pool status %d\n", Status ));
///
return EFI_INVALID_PARAMETER;
}
/**
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;
PCH_SMM_QUALIFIED_PROTOCOL *Qualified;
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);
}
if (This != NULL) {
UINTN ContextSize;
Qualified = QUALIFIED_PROTOCOL_FROM_GENERIC (This);
switch (Qualified->Type) {
case UsbType:
ContextSize = sizeof(RecordToDelete->ChildContext.Usb);
break;
case SxType:
ContextSize = sizeof(RecordToDelete->ChildContext.Sx);
break;
case SwType:
ContextSize = sizeof(RecordToDelete->ChildContext.Sw);
break;
case GpiType:
ContextSize = sizeof(RecordToDelete->ChildContext.Gpi);
break;
case PowerButtonType:
ContextSize = sizeof(RecordToDelete->ChildContext.PowerButton);
break;
case PeriodicTimerType:
ContextSize = sizeof(RecordToDelete->ChildContext.PeriodicTimer);
break;
default:
ASSERT(FALSE);
ContextSize = 0;
break;
}
SmiHandlerProfileUnregisterHandler (Qualified->Guid, RecordToDelete->Callback, &RecordToDelete->ChildContext, ContextSize);
} else {
if (RecordToDelete->ProtocolGuid != NULL) {
SmiHandlerProfileUnregisterHandler (RecordToDelete->ProtocolGuid, (EFI_SMM_HANDLER_ENTRY_POINT2)RecordToDelete->PchSmiCallback, &RecordToDelete->PchSmiType, sizeof(RecordToDelete->PchSmiType));
}
}
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
**/
static
VOID
ClearPendingSmiStatus (
UINT32 SmiStsValue
)
{
//
// Clear NewCentury status if it's not handled.
//
if (SmiStsValue & B_PCH_SMI_STS_TCO) {
PchTcoClearNewCenturySts (NULL);
}
// Clear PWRBTNOR_STS if it's not handled.
//
if (IoRead16 (mAcpiBaseAddr + R_PCH_ACPI_PM1_STS) & B_PCH_ACPI_PM1_STS_PRBTNOR) {
IoWrite16 (mAcpiBaseAddr + R_PCH_ACPI_PM1_STS, B_PCH_ACPI_PM1_STS_PRBTNOR);
}
//
// Clear SWSMI if this is triggered by SmmControl.
//
if (SmiStsValue & B_PCH_SMI_STS_APM) {
IoWrite32 (mAcpiBaseAddr + R_PCH_SMI_STS, B_PCH_SMI_STS_APM);
}
}
/**
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_PCH_RTC_EXT_INDEX_ALT);
Port74Save = IoRead8 (R_PCH_RTC_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_PCH_SMI_EN));
SmiStsValue = IoRead32 ((UINTN) (mAcpiBaseAddr + R_PCH_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);
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);
///
/// 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_PCH_RTC_EXT_INDEX_ALT, Port76Save);
IoWrite8 (R_PCH_RTC_INDEX_ALT, Port74Save);
return Status;
}