diff options
Diffstat (limited to 'ReferenceCode/Chipset/LynxPoint/PchSmiDispatcher/Smm/PchSmmCore.c')
-rw-r--r-- | ReferenceCode/Chipset/LynxPoint/PchSmiDispatcher/Smm/PchSmmCore.c | 891 |
1 files changed, 891 insertions, 0 deletions
diff --git a/ReferenceCode/Chipset/LynxPoint/PchSmiDispatcher/Smm/PchSmmCore.c b/ReferenceCode/Chipset/LynxPoint/PchSmiDispatcher/Smm/PchSmmCore.c new file mode 100644 index 0000000..89100d5 --- /dev/null +++ b/ReferenceCode/Chipset/LynxPoint/PchSmiDispatcher/Smm/PchSmmCore.c @@ -0,0 +1,891 @@ +/** @file + This driver is responsible for the registration of child drivers + and the abstraction of the PCH SMI sources. + +@copyright + Copyright (c) 1999 - 2012 Intel Corporation. All rights reserved + This software and associated documentation (if any) is furnished + under a license and may only be used or copied in accordance + with the terms of the license. Except as permitted by such + license, no part of this software or documentation may be + reproduced, stored in a retrieval system, or transmitted in any + form or by any means without the express written consent of + Intel Corporation. + + This file contains an 'Intel Peripheral Driver' and uniquely + identified as "Intel Reference Module" and is + licensed for Intel CPUs and chipsets under the terms of your + license agreement with Intel or your vendor. This file may + be modified by the user, subject to additional terms of the + license agreement + +**/ +#include "PchSmm.h" +#include "PchSmmHelpers.h" +// AMI_OVERRIDE, GPIO_ROUT >>> +#include "Token.h" +// AMI_OVERRIDE, GPIO_ROUT <<< + +/// +/// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/// MODULE / GLOBAL DATA +/// +/// Module variables used by the both the main dispatcher and the source dispatchers +/// Declared in PchSmmSources.h +/// +EFI_SMM_SYSTEM_TABLE *mSmst; +UINT32 mAcpiBaseAddr; +UINT32 mGpioBaseAddr; +EFI_SMM_BASE_PROTOCOL *mSmmBase; +// +// The reserved mmio address can only be used in Sx handler +// it's not reserved in ACPI. +// +EFI_PHYSICAL_ADDRESS mResvMmioBaseAddr; + +PRIVATE_DATA mPrivateData = { ///< for the structure + { + NULL + }, ///< CallbackDataBase linked list head + NULL, ///< EFI handle returned when calling InstallMultipleProtocolInterfaces + { ///< protocol arrays + /// + /// elements within the array + /// + { + PROTOCOL_SIGNATURE, + UsbType, + { + (PCH_SMM_GENERIC_REGISTER) PchSmmCoreRegister, + (PCH_SMM_GENERIC_UNREGISTER) PchSmmCoreUnRegister + } + }, + { + PROTOCOL_SIGNATURE, + SxType, + { + (PCH_SMM_GENERIC_REGISTER) PchSmmCoreRegister, + (PCH_SMM_GENERIC_UNREGISTER) PchSmmCoreUnRegister + } + }, + { + PROTOCOL_SIGNATURE, + SwType, + { + (PCH_SMM_GENERIC_REGISTER) PchSmmCoreRegister, + (PCH_SMM_GENERIC_UNREGISTER) PchSmmCoreUnRegister, + (UINTN) MAXIMUM_SWI_VALUE + } + }, + { + PROTOCOL_SIGNATURE, + GpiType, + { + (PCH_SMM_GENERIC_REGISTER) PchSmmCoreRegister, + (PCH_SMM_GENERIC_UNREGISTER) PchSmmCoreUnRegister, + (UINTN) NUM_SUPPORTED_GPIS + } + }, + { + PROTOCOL_SIGNATURE, + IchnType, + { + (PCH_SMM_GENERIC_REGISTER) PchSmmCoreRegister, + (PCH_SMM_GENERIC_UNREGISTER) PchSmmCoreUnRegister + } + }, + { + PROTOCOL_SIGNATURE, + IchnExType, + { + (PCH_SMM_GENERIC_REGISTER) PchSmmCoreRegister, + (PCH_SMM_GENERIC_UNREGISTER) PchSmmCoreUnRegister + } + }, + { + PROTOCOL_SIGNATURE, + PowerButtonType, + { + (PCH_SMM_GENERIC_REGISTER) PchSmmCoreRegister, + (PCH_SMM_GENERIC_UNREGISTER) PchSmmCoreUnRegister + } + }, + { + PROTOCOL_SIGNATURE, + PeriodicTimerType, + { + (PCH_SMM_GENERIC_REGISTER) PchSmmCoreRegister, + (PCH_SMM_GENERIC_UNREGISTER) PchSmmCoreUnRegister, + (UINTN) PchSmmPeriodicTimerDispatchGetNextShorterInterval + } + }, + } +}; + +CONTEXT_FUNCTIONS mContextFunctions[PchSmmProtocolTypeMax] = { + { + NULL, + NULL + }, + { + SxGetContext, + SxCmpContext + }, + { + SwGetContext, + SwCmpContext + }, + { + NULL, + NULL + }, + { + NULL, + NULL + }, + { + NULL, + NULL + }, + { + PowerButtonGetContext, + PowerButtonCmpContext + }, + { + PeriodicTimerGetContext, + PeriodicTimerCmpContext + }, +}; + +/// +/// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/// PROTOTYPES +/// +/// Functions use only in this file +/// +EFI_STATUS +EFIAPI +PchSmmCoreDispatcher ( + IN EFI_HANDLE SmmImageHandle, + IN OUT VOID *CommunicationBuffer, + IN OUT UINTN *SourceSize + ); + +/// +/// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/// FUNCTIONS +/// +/// Driver entry point +/// + +/** + Initializes the PCH SMM Dispatcher + + @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; + + /// + /// Locate SMM Base Protocol + /// + Status = gBS->LocateProtocol (&gEfiSmmBaseProtocolGuid, NULL, (VOID **) &mSmmBase); + if (EFI_ERROR (Status)) { + ASSERT (FALSE); + return Status; + } + /// + /// Initialize our module variables + /// + Status = mSmmBase->GetSmstLocation (mSmmBase, &mSmst); + if (EFI_ERROR (Status)) { + ASSERT (FALSE); + return Status; + } + + PERF_START_EX (ImageHandle, L"SmmModule", NULL, AsmReadTsc(), 8); + + /// + /// Access ACPI Base Addresses Register + /// + mAcpiBaseAddr = (UINT32) (MmioRead16 ( + MmPciAddress (0, + DEFAULT_PCI_BUS_NUMBER_PCH, + PCI_DEVICE_NUMBER_PCH_LPC, + PCI_FUNCTION_NUMBER_PCH_LPC, + R_PCH_LPC_ACPI_BASE) + ) & B_PCH_LPC_ACPI_BASE_BAR); + ASSERT (mAcpiBaseAddr != 0); + + /// + /// Access GPIO Base Addresses Register + /// + mGpioBaseAddr = (UINT32) (MmioRead16 ( + MmPciAddress (0, + DEFAULT_PCI_BUS_NUMBER_PCH, + PCI_DEVICE_NUMBER_PCH_LPC, + PCI_FUNCTION_NUMBER_PCH_LPC, + R_PCH_LPC_GPIO_BASE) + ) & B_PCH_LPC_GPIO_BASE_BAR); + ASSERT (mGpioBaseAddr != 0); + + /// + /// Fix the ICHN_SOURCE_DESCS base, in order to obtain independence with platform. + /// + PchSmmIchnFixSourceBase (); + + PchSmmPublishDispatchProtocols (); + + /// + /// Register a callback function to handle subsequent SMIs. This callback + /// will be called by SmmCoreDispatcher. + /// +//AMI_OVERRIDE Support SMM2>> + mSmmBase->RegisterCallback (mSmmBase, ImageHandle, PchSmmCoreDispatcher, TRUE, FALSE); +//AMI_OVERRIDE Support SMM2<< + + /// + /// Initialize Callback DataBase + /// + InitializeListHead (&mPrivateData.CallbackDataBase); + + /// + /// Enable SMIs on the PCH now that we have a callback + /// + PchSmmInitHardware (); + + mResvMmioBaseAddr = 0x0ffffffff; +#ifndef AMI_OVERRIDE_FOR_PCH + Status = gDS->AllocateMemorySpace ( + EfiGcdAllocateMaxAddressSearchTopDown, + EfiGcdMemoryTypeMemoryMappedIo, + 16, // 2^16: 64K Alignment + 0x10000, // 64K Length + &mResvMmioBaseAddr, + ImageHandle, + NULL + ); +#else + Status = gDS->AllocateMemorySpace ( + EfiGcdAllocateMaxAddressSearchBottomUp, + EfiGcdMemoryTypeMemoryMappedIo, + 16, // 2^16: 64K Alignment + 0x10000, // 64K Length + &mResvMmioBaseAddr, + ImageHandle, + NULL + ); +#endif + ASSERT_EFI_ERROR (Status); + DEBUG((EFI_D_INFO,"mResvMmioBaseAddr %x\n", mResvMmioBaseAddr)); + + 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 +PchSmmCoreRegister ( + IN PCH_SMM_GENERIC_PROTOCOL *This, + IN PCH_SMM_CALLBACK 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 = NULL_SOURCE_DESC_INITIALIZER; + PCH_SERIES PchSeries; + UINTN Index; + + PchSeries = GetPchSeries(); + Index = 0; + /// + /// Create database record and add to database + /// + if (mSmst == NULL) { + ASSERT (FALSE); + return EFI_OUT_OF_RESOURCES; + } + + Status = mSmst->SmmAllocatePool (EfiRuntimeServicesData, sizeof (DATABASE_RECORD), (VOID **) &Record); + if (EFI_ERROR (Status)) { + ASSERT (FALSE); + return EFI_OUT_OF_RESOURCES; + } + /// + /// 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; + + switch (Qualified->Type) { + /// + /// By the end of this switch statement, we'll know the + /// source description the child is registering for + /// + case UsbType: + /// + /// Check the validity of Context Type + /// + if ((Record->ChildContext.Usb.Type < UsbLegacy) || (Record->ChildContext.Usb.Type > UsbWake)) { + goto Error; + } + + InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link); + MapUsbToSrcDesc (DispatchContext, &(Record->SrcDesc)); + Record->ClearSource = NULL; + /// + /// use default clear source function + /// + break; + + case SxType: + /// + /// 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; + } + + InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link); + CopyMem ((VOID *) &(Record->SrcDesc), (VOID *) (&SX_SOURCE_DESC), sizeof (PCH_SMM_SOURCE_DESC)); + Record->ClearSource = NULL; + /// + /// use default clear source function + /// + break; + + case SwType: + /// + /// Check the validity of Context Value + /// + if (Record->ChildContext.Sw.SwSmiInputValue > MAXIMUM_SWI_VALUE) { + goto Error; + } + + if (EFI_ERROR (SmiInputValueDuplicateCheck (Record->ChildContext.Sw.SwSmiInputValue))) { + goto Error; + } + + InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link); + CopyMem ((VOID *) &(Record->SrcDesc), (VOID *) (&SW_SOURCE_DESC), sizeof (PCH_SMM_SOURCE_DESC)); + Record->ClearSource = NULL; + /// + /// use default clear source function + /// + break; + + case GpiType: + Index = Record->ChildContext.Gpi.GpiNum; + if (PchSeries == PchH) { + Index -= V_PCH_LPTH_ALT_GP_SMI_GPIBASE; + if (Index >= S_PCH_LPTH_ALT_GP_SMI_GPISIZE) { + goto Error; + } + } else if (PchSeries == PchLp) { + Index -= V_PCH_LPTLP_ALT_GP_SMI_GPIBASE; + if (Index >= S_PCH_LPTLP_ALT_GP_SMI_GPISIZE) { + goto Error; + } + } else { + goto Error; + } + InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link); + if (PchSeries == PchH) { + CopyMem ( + (VOID *) &(Record->SrcDesc), + (VOID *) &(LPTH_GPI_SOURCE_DESC[Index]), + sizeof (PCH_SMM_SOURCE_DESC) + ); + } else if (PchSeries == PchLp) { + CopyMem ( + (VOID *) &(Record->SrcDesc), + (VOID *) &(LPTLP_GPI_SOURCE_DESC[Index]), + sizeof (PCH_SMM_SOURCE_DESC) + ); + } + Record->ClearSource = NULL; + /// + /// use default clear source function + /// + break; + + case IchnType: + /// + /// Check the validity of Context Type + /// + if (Record->ChildContext.Ichn.Type >= NUM_ICHN_TYPES) { + goto Error; + } + + InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link); + if (PchSeries == PchLp) { + CopyMem ( + (VOID *) &(Record->SrcDesc), + (VOID *) &(ICHN_LP_SOURCE_DESCS[Record->ChildContext.Ichn.Type]), + sizeof (PCH_SMM_SOURCE_DESC) + ); + } else if (PchSeries == PchH) { + CopyMem ( + (VOID *) &(Record->SrcDesc), + (VOID *) &(ICHN_H_SOURCE_DESCS[Record->ChildContext.Ichn.Type]), + sizeof (PCH_SMM_SOURCE_DESC) + ); + } + Record->ClearSource = PchSmmIchnClearSource; + break; + + case IchnExType: + /// + /// Check the validity of Context Type + /// + if ((Record->ChildContext.IchnEx.Type < IchnExPciExpress) || (Record->ChildContext.IchnEx.Type >= IchnExTypeMAX)) { + goto Error; + } + + InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link); + CopyMem ( + (VOID *) &(Record->SrcDesc), + (VOID *) &(ICHN_EX_SOURCE_DESCS[Record->ChildContext.IchnEx.Type - IchnExPciExpress]), + sizeof (PCH_SMM_SOURCE_DESC) + ); + Record->ClearSource = NULL; + break; + + case PowerButtonType: + /// + /// Check the validity of Context Phase + /// + if ((Record->ChildContext.PowerButton.Phase < PowerButtonEntry) || + (Record->ChildContext.PowerButton.Phase > PowerButtonExit) + ) { + goto Error; + } + + InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link); + CopyMem ((VOID *) &(Record->SrcDesc), (VOID *) &POWER_BUTTON_SOURCE_DESC, sizeof (PCH_SMM_SOURCE_DESC)); + Record->ClearSource = NULL; + /// + /// use default clear source function + /// + break; + + case PeriodicTimerType: + /// + /// Check the validity of timer value + /// + if (DispatchContext->PeriodicTimer.SmiTickInterval <= 0) { + goto Error; + } + + InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link); + MapPeriodicTimerToSrcDesc (DispatchContext, &(Record->SrcDesc)); + Record->ClearSource = PchSmmPeriodicTimerClearSource; + break; + + default: + return EFI_INVALID_PARAMETER; + break; + } + + if (CompareSources (&Record->SrcDesc, &NullSourceDesc)) { + goto Error; + } + + 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); + +// AMI_OVERRIDE, GPIO_ROUT >>> +// Added GPIO_ROUT (B0:D31:F0 Reg#B8h) initialization for GPI SMI register. +{ + UINT32 GpioRout; + UINT32 GpioSmiRout; + + if (Qualified->Type == GpiType) { + if (PchSeries == PchH) { + GpioRout = (UINT32) (MmioRead32 ( \ + MmPciAddress (0, + DEFAULT_PCI_BUS_NUMBER_PCH, \ + PCI_DEVICE_NUMBER_PCH_LPC, \ + PCI_FUNCTION_NUMBER_PCH_LPC, \ + R_PCH_LPC_GPI_ROUT)\ + )); + + GpioRout &= ~(3 << (Record->ChildContext.Gpi.GpiNum * 2)); + GpioRout |= (1 << (Record->ChildContext.Gpi.GpiNum * 2)); + + MmioWrite32 ( \ + MmPciAddress (0, + DEFAULT_PCI_BUS_NUMBER_PCH, \ + PCI_DEVICE_NUMBER_PCH_LPC, \ + PCI_FUNCTION_NUMBER_PCH_LPC,\ + R_PCH_LPC_GPI_ROUT), \ + GpioRout\ + ); + }else if(PchSeries == PchLp){ + if( Record->ChildContext.Gpi.GpiNum >= 32 && Record->ChildContext.Gpi.GpiNum < 48){ + GpioRout = IoRead32(GPIO_BASE_ADDRESS + R_PCH_LP_LPC_GPI_ROUT1); + GpioSmiRout = IoRead32(GPIO_BASE_ADDRESS + R_PCH_LPTLP_ALT_GP_SMI_EN); + + GpioRout |= (1 << (Record->ChildContext.Gpi.GpiNum - 32)); + GpioSmiRout |= (1 << (Record->ChildContext.Gpi.GpiNum - 32)); + + IoWrite32((GPIO_BASE_ADDRESS + R_PCH_LP_LPC_GPI_ROUT1),GpioRout); + IoWrite32((GPIO_BASE_ADDRESS + R_PCH_LPTLP_ALT_GP_SMI_EN),GpioSmiRout); + } + } + } +} +// AMI_OVERRIDE, GPIO_ROUT <<< + + /// + /// Child's handle will be the address linked list link in the record + /// + *DispatchHandle = (EFI_HANDLE) (&Record->Link); + + return EFI_SUCCESS; + +Error: + Status = mSmst->SmmFreePool (Record); + /// + /// DEBUG((EFI_D_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 EFI_SMM_IO_TRAP_DISPATCH_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 +PchSmmCoreUnRegister ( + IN PCH_SMM_GENERIC_PROTOCOL *This, + IN EFI_HANDLE *DispatchHandle + ) +{ + /// + /// BOOLEAN SafeToDisable; + /// + DATABASE_RECORD *RecordToDelete; + + /// + /// DATABASE_RECORD *RecordInDb; + /// EFI_LIST_NODE *LinkInDb; + /// + if (DispatchHandle == NULL) { + return EFI_INVALID_PARAMETER; + } + + 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); + + /// + /// See if we can disable the source, reserved for future use since this might + /// not be the only criteria to disable + /// + /// SafeToDisable = TRUE; + /// LinkInDb = GetFirstNode (&mPrivateData.CallbackDataBase); + /// while(!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) { + /// RecordInDb = DATABASE_RECORD_FROM_LINK (LinkInDb); + /// if (CompareEnables (&RecordToDelete->SrcDesc, &RecordInDb->SrcDesc)) { + /// SafeToDisable = FALSE; + /// break; + /// } + /// LinkInDb = GetNextNode (&mPrivateData.CallbackDataBase, &RecordInDb->Link); + /// } + /// if (SafeToDisable) { + /// PchSmmDisableSource( &Record->SrcDesc ); + /// } + /// + return EFI_SUCCESS; +} + +/** + The callback function to handle subsequent SMIs. This callback will be called by SmmCoreDispatcher. + + @param[in] SmmImageHandle 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 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; + + EFI_STATUS Status; + + PCH_SMM_SOURCE_DESC ActiveSource = NULL_SOURCE_DESC_INITIALIZER; + + EscapeCount = 100; + ContextsMatch = FALSE; + EosSet = FALSE; + SxChildWasDispatched = FALSE; + Status = EFI_SUCCESS; + + if (!IsListEmpty (&mPrivateData.CallbackDataBase)) { + /// + /// We have children registered w/ us -- continue + /// + while ((!EosSet) && (EscapeCount > 0)) { + EscapeCount--; + + LinkInDb = GetFirstNode (&mPrivateData.CallbackDataBase); + + while (!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) { + RecordInDb = DATABASE_RECORD_FROM_LINK (LinkInDb); + + /// + /// look for the first active source + /// + if (!SourceIsActive (&RecordInDb->SrcDesc)) { + /// + /// 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)) { + 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. + /// + ASSERT (RecordToExhaust->Callback != NULL); + Context = RecordToExhaust->ChildContext; + ContextsMatch = TRUE; + } + + if (ContextsMatch) { + + ASSERT (RecordToExhaust->Callback != NULL); + PERF_START_EX (NULL, L"SmmFunction", NULL, AsmReadTsc(), RecordToExhaust->ProtocolType); + RecordToExhaust->Callback ((EFI_HANDLE) & RecordToExhaust->Link, &Context); + PERF_END_EX (NULL, L"SmmFunction", NULL, AsmReadTsc(), RecordToExhaust->ProtocolType); + if (RecordToExhaust->ProtocolType == SxType) { + SxChildWasDispatched = TRUE; + } + } + } + } + + 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); + } + /// + /// 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 (); + } + + return Status; + +} |