From 40039e28edd6f88614c0c0261bf78e74fde73bf0 Mon Sep 17 00:00:00 2001 From: mdkinney Date: Fri, 11 Feb 2011 00:09:16 +0000 Subject: Add SmmPeriodicSmiLib to MdePkg. git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@11302 6f19259b-4bc3-4df7-8a09-765794883524 --- .../Library/SmmPeriodicSmiLib/SmmPeriodicSmiLib.c | 1165 ++++++++++++++++++++ .../SmmPeriodicSmiLib/SmmPeriodicSmiLib.inf | 50 + 2 files changed, 1215 insertions(+) create mode 100644 MdePkg/Library/SmmPeriodicSmiLib/SmmPeriodicSmiLib.c create mode 100644 MdePkg/Library/SmmPeriodicSmiLib/SmmPeriodicSmiLib.inf (limited to 'MdePkg/Library') diff --git a/MdePkg/Library/SmmPeriodicSmiLib/SmmPeriodicSmiLib.c b/MdePkg/Library/SmmPeriodicSmiLib/SmmPeriodicSmiLib.c new file mode 100644 index 0000000000..a941d75e5f --- /dev/null +++ b/MdePkg/Library/SmmPeriodicSmiLib/SmmPeriodicSmiLib.c @@ -0,0 +1,1165 @@ +/** @file + SMM Periodic SMI Library. + + Copyright (c) 2011, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +/// +/// Define the number of periodic SMI handler entries that should be allocated in +/// the constructor for gPeriodicSmiLibraryHandlers and also use this value as the +/// number of entries to add to gPeriodicSmiLibraryHandlers when gPeriodicSmiLibraryHandlers +/// is full. +/// +#define PERIODIC_SMI_LIBRARY_ALLOCATE_SIZE 0x08 + +/// +/// Signature for a PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT structure +/// +#define PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT_SIGNATURE SIGNATURE_32 ('P', 'S', 'M', 'I') + +/// +/// Structure that contains state information for an enabled periodic SMI handler +/// +typedef struct { + /// + /// Signature value that must be set to PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT_SIGNATURE + /// + UINT32 Signature; + /// + /// The dispatch function to called to invoke an enabled periodic SMI handler. + /// + PERIODIC_SMI_LIBRARY_HANDLER DispatchFunction; + /// + /// The context to pass into DispatchFunction + /// + VOID *Context; + /// + /// The tick period in 100 ns units that DispatchFunction should be called. + /// + UINT64 TickPeriod; + /// + /// The Cpu number that is required to execute DispatchFunction. If Cpu is + /// set to PERIODIC_SMI_LIBRARY_ANY_CPU, then DispatchFunction may be executed + /// on any CPU. + /// + UINTN Cpu; + /// + /// The size, in bytes, of the stack allocated for a periodic SMI handler. + /// This value must be a multiple of EFI_PAGE_SIZE. + /// + UINTN StackSize; + /// + /// A pointer to the stack allocated using AllocatePages(). This field will + /// be NULL if StackSize is 0. + /// + VOID *Stack; + /// + /// Spin lock used to wait for an AP to complete the execution of a periodic SMI handler + /// + SPIN_LOCK DispatchLock; + /// + /// The rate in Hz of the performance counter that is used to measure the + /// amount of time that a periodic SMI handler executes. + /// + UINT64 PerfomanceCounterRate; + /// + /// The start count value of the performance counter that is used to measure + /// the amount of time that a periodic SMI handler executes. + /// + UINT64 PerfomanceCounterStartValue; + /// + /// The end count value of the performance counter that is used to measure + /// the amount of time that a periodic SMI handler executes. + /// + UINT64 PerfomanceCounterEndValue; + /// + /// The context record passed into the Register() function of the SMM Periodic + /// Timer Dispatch Protocol when a periodic SMI handler is enabled. + /// + EFI_SMM_PERIODIC_TIMER_REGISTER_CONTEXT RegisterContext; + /// + /// The handle returned from the Register() function of the SMM Periodic + /// Timer Dispatch Protocol when a periodic SMI handler is enabled. + /// + EFI_HANDLE DispatchHandle; + /// + /// The total number of performance counter ticks that the periodic SMI handler + /// has been executing in its current invocation. + /// + UINT64 DispatchTotalTime; + /// + /// The performance counter value that was captured the last time that the + /// periodic SMI handler called PeriodcSmiExecutionTime(). This allows the + /// time value returned by PeriodcSmiExecutionTime() to be accurate even when + /// the performance counter rolls over. + /// + UINT64 DispatchCheckPointTime; + /// + /// Buffer used to save the context when control is transfer from this library + /// to an enabled periodic SMI handler. This saved context is used when the + /// periodic SMI handler exits or yields. + /// + BASE_LIBRARY_JUMP_BUFFER DispatchJumpBuffer; + /// + /// Flag that is set to TRUE when a periodic SMI handler requests to yield + /// using PeriodicSmiYield(). When this flag IS TRUE, YieldJumpBuffer is + /// valid. When this flag is FALSE, YieldJumpBuffer is not valid. + /// + BOOLEAN YieldFlag; + /// + /// Buffer used to save the context when a periodic SMI handler requests to + /// yield using PeriodicSmiYield(). This context is used to resume the + /// execution of a periodic SMI handler the next time control is transferd + /// to the periodic SMI handler that yielded. + /// + BASE_LIBRARY_JUMP_BUFFER YieldJumpBuffer; + /// + /// The amount of time, in 100 ns units, that have elapsed since the last + /// time the periodic SMI handler was invoked. + /// + UINT64 ElapsedTime; +} PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT; + +/** + Macro that returns a pointer to a PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT + structure based on a pointer to a RegisterContext field. + +**/ +#define PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT_FROM_REGISTER_CONTEXT(a) \ + CR ( \ + a, \ + PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT, \ + RegisterContext, \ + PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT_SIGNATURE \ + ) + +/// +/// Pointer to the SMM Periodic Timer Disatch Protocol that was located in the constuctor. +/// +EFI_SMM_PERIODIC_TIMER_DISPATCH2_PROTOCOL *gSmmPeriodicTimerDispatch2 = NULL; + +/// +/// Pointer to a table of supported periodic SMI tick periods in 100 ns units +/// sorted from largest to smallest terminated by a tick period value of 0. +/// This table is allocated using AllocatePool() in the constructor and filled +/// in based on the values returned from the SMM Periodic Timer Dispatch 2 Protocol +/// function GetNextShorterInterval(). +/// +UINT64 *gSmiTickPeriodTable = NULL; + +/// +/// The number entries in gPeriodicSmiLibraryHandlers +/// +UINTN gNumberOfPeriodicSmiLibraryHandlers = 0; + +/// +/// Table of periodic SMI handlers that this library is currently managing. This +/// table is allocated using AllocatePool() +/// +PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *gPeriodicSmiLibraryHandlers = NULL; + +/// +/// The index of gPeriodicSmiLibraryHandlers that is currently being executed. +/// Is set to -1 if no periodic SMI handler is currently being executed. +/// +INTN gActivePeriodicSmiLibraryHandlerIndex = -1; + +/** + Internal worker function that returns a pointer to the + PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT structure associated with the periodic + SMI handler that is currently being executed. If a periodic SMI handler is + not currently being executed, the NULL is returned. + + @retval NULL A periodic SMI handler is not currently being executed. + @retval other Pointer to the PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT + associated with the active periodic SMI handler. + +**/ +PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT * +GetActivePeriodicSmiLibraryHandler ( + VOID + ) +{ + if (gActivePeriodicSmiLibraryHandlerIndex < 0) { + // + // Return NULL if index is negative, which means that there is no active + // periodic SMI handler. + // + return NULL; + } + + // + // Return a pointer to the active periodic SMI handler context + // + return &gPeriodicSmiLibraryHandlers[gActivePeriodicSmiLibraryHandlerIndex]; +} + +/** + Internal worker function that returns a pointer to the + PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT structure associated with the + DispatchHandle that was returned when the periodic SMI handler was enabled + with PeriodicSmiEnable(). If DispatchHandle is NULL, then the active + periodic SMI handler is returned. If DispatchHandle is NULL and there is + no active periodic SMI handler, then NULL is returned. + + @param[in] DispatchHandle DispatchHandle that was returned when the periodic + SMI handler was enabled with PeriodicSmiEnable(). + This is an optional parameter that may be NULL. + If this parameter is NULL, then the active periodic + SMI handler is returned. + + @retval NULL DispatchHandle is NULL and there is no active periodic SMI + handler. + @retval NULL DispatchHandle does not match any of the periodic SMI handlers + that have been enabled with PeriodicSmiEnable(). + @retval other Pointer to the PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT + associated with the DispatchHandle. + +**/ +PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT * +LookupPeriodicSmiLibraryHandler ( + IN EFI_HANDLE DispatchHandle OPTIONAL + ) +{ + UINTN Index; + + // + // If DispatchHandle is NULL, then return the active periodic SMI handler + // + if (DispatchHandle == NULL) { + return GetActivePeriodicSmiLibraryHandler (); + } + + // + // Search the periodic SMI handler entries for a a matching DispatchHandle + // + for (Index = 0; Index < gNumberOfPeriodicSmiLibraryHandlers; Index++) { + if (gPeriodicSmiLibraryHandlers[Index].DispatchHandle == DispatchHandle) { + return &gPeriodicSmiLibraryHandlers[Index]; + } + } + + // + // No entries match DispatchHandle, so return NULL + // + return NULL; +} + +/** + Internal worker function that sets that active periodic SMI handler based on + the Context used when the periodic SMI handler was registered with the + SMM Periodic Timer Dispatch 2 Protocol. If Context is NULL, then the + state is updated to show that there is not active periodic SMI handler. + A pointer to the active PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT structure + is returned. + + @retval NULL Context is NULL. + @retval other Pointer to the PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT + associated with Context. + +**/ +PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT * +SetActivePeriodicSmiLibraryHandler ( + IN CONST VOID *Context OPTIONAL + ) +{ + PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *PeriodicSmiLibraryHandler; + + if (Context == NULL) { + gActivePeriodicSmiLibraryHandlerIndex = -1; + return NULL; + } + PeriodicSmiLibraryHandler = PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT_FROM_REGISTER_CONTEXT (Context); + gActivePeriodicSmiLibraryHandlerIndex = PeriodicSmiLibraryHandler - gPeriodicSmiLibraryHandlers; + return PeriodicSmiLibraryHandler; +} + +/** + Internal worker function that returns a free entry for a new periodic + SMI handler. If no free entries are available, then additional + entries are allocated. + + @retval NULL There are not enough resources available to to allocate a free entry. + @retval other Pointer to a free PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT structure. + +**/ +PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT * +FindFreePeriodicSmiLibraryHandler ( + VOID + ) +{ + UINTN Index; + PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *PeriodicSmiLibraryHandler; + + // + // Search for a free entry in gPeriodicSmiLibraryHandlers + // A free entry must have a NULL DispatchHandle and a NULL Stack. + // + for (Index = 0; Index < gNumberOfPeriodicSmiLibraryHandlers; Index++) { + if (gPeriodicSmiLibraryHandlers[Index].DispatchHandle != NULL) { + continue; + } + if (gPeriodicSmiLibraryHandlers[Index].Stack != NULL) { + continue; + } + return &gPeriodicSmiLibraryHandlers[Index]; + } + + // + // If no free entries were found, then grow the table of periodic SMI handler entries + // + if (Index == gNumberOfPeriodicSmiLibraryHandlers) { + PeriodicSmiLibraryHandler = ReallocatePool ( + gNumberOfPeriodicSmiLibraryHandlers * sizeof (PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT), + (gNumberOfPeriodicSmiLibraryHandlers + PERIODIC_SMI_LIBRARY_ALLOCATE_SIZE) * sizeof (PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT), + gPeriodicSmiLibraryHandlers + ); + if (PeriodicSmiLibraryHandler == NULL) { + return NULL; + } + gPeriodicSmiLibraryHandlers = PeriodicSmiLibraryHandler; + gNumberOfPeriodicSmiLibraryHandlers += PERIODIC_SMI_LIBRARY_ALLOCATE_SIZE; + } + + return &gPeriodicSmiLibraryHandlers[Index]; +} + +/** + This function returns a pointer to a table of supported periodic + SMI tick periods in 100 ns units sorted from largest to smallest. + The table contains a array of UINT64 values terminated by a tick + period value of 0. The returned table must be treated as read-only + data and must not be freed. + + @return A pointer to a table of UINT64 tick period values in + 100ns units sorted from largest to smallest terminated + by a tick period of 0. + +**/ +UINT64 * +EFIAPI +PeriodicSmiSupportedTickPeriod ( + VOID + ) +{ + // + // Return the table allocated and populated by SmmPeriodicSmiLibConstructor() + // + return gSmiTickPeriodTable; +} + +/** + This function returns the time in 100ns units since the periodic SMI + handler function was called. If the periodic SMI handler was resumed + through PeriodicSmiYield(), then the time returned is the time in + 100ns units since PeriodicSmiYield() returned. + + @return The actual time in 100ns units that the periodic SMI handler + has been executing. If this function is not called from within + an enabled periodic SMI handler, then 0 is returned. + +**/ +UINT64 +EFIAPI +PeriodicSmiExecutionTime ( + VOID + ) +{ + PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *PeriodicSmiLibraryHandler; + UINT64 Current; + UINT64 Count; + + // + // If there is no active periodic SMI handler, then return 0 + // + PeriodicSmiLibraryHandler = GetActivePeriodicSmiLibraryHandler (); + if (PeriodicSmiLibraryHandler == NULL) { + return 0; + } + + // + // Get the current performance counter value + // + Current = GetPerformanceCounter (); + + // + // Count the number of performance counter ticks since the periodic SMI handler + // was dispatched or the last time this function was called. + // + if (PeriodicSmiLibraryHandler->PerfomanceCounterEndValue > PeriodicSmiLibraryHandler->PerfomanceCounterStartValue) { + // + // The performance counter counts up. Check for roll over condition. + // + if (Current > PeriodicSmiLibraryHandler->DispatchCheckPointTime) { + Count = Current - PeriodicSmiLibraryHandler->DispatchCheckPointTime; + } else { + Count = (Current - PeriodicSmiLibraryHandler->PerfomanceCounterStartValue) + (PeriodicSmiLibraryHandler->PerfomanceCounterEndValue - PeriodicSmiLibraryHandler->DispatchCheckPointTime); + } + } else { + // + // The performance counter counts down. Check for roll over condition. + // + if (PeriodicSmiLibraryHandler->DispatchCheckPointTime > Current) { + Count = PeriodicSmiLibraryHandler->DispatchCheckPointTime - Current; + } else { + Count = (PeriodicSmiLibraryHandler->DispatchCheckPointTime - PeriodicSmiLibraryHandler->PerfomanceCounterEndValue) + (PeriodicSmiLibraryHandler->PerfomanceCounterStartValue - Current); + } + } + + // + // Accumulate the total number of performance counter ticks since the periodic + // SMI handler was dispatched or resumed. + // + PeriodicSmiLibraryHandler->DispatchTotalTime += Count; + + // + // Update the checkpoint value to the current performance counter value + // + PeriodicSmiLibraryHandler->DispatchCheckPointTime = Current; + + // + // Convert the total number of performance counter ticks to 100 ns units + // + return DivU64x64Remainder ( + MultU64x32 (PeriodicSmiLibraryHandler->DispatchTotalTime, 10000000), + PeriodicSmiLibraryHandler->PerfomanceCounterRate, + NULL + ); +} + +/** + This function returns control back to the SMM Foundation. When the next + periodic SMI for the currently executing handler is triggered, the periodic + SMI handler will restarted from its registered DispatchFunction entry point. + If this function is not called from within an enabled periodic SMI handler, + then control is returned to the calling function. + +**/ +VOID +EFIAPI +PeriodicSmiExit ( + VOID + ) +{ + PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *PeriodicSmiLibraryHandler; + + // + // If there is no active periodic SMI handler, then return + // + PeriodicSmiLibraryHandler = GetActivePeriodicSmiLibraryHandler (); + if (PeriodicSmiLibraryHandler == NULL) { + return; + } + + // + // Perform a long jump back to the point when the currently executing dispatch + // function was dispatched. + // + LongJump (&PeriodicSmiLibraryHandler->DispatchJumpBuffer, 1); + + // + // Must never return + // + ASSERT (FALSE); + CpuDeadLoop(); +} + +/** + This function yields control back to the SMM Foundation. When the next + periodic SMI for the currently executing handler is triggered, the periodic + SMI handler will be resumed and this function will return. Use of this + function requires a seperate stack for the periodic SMI handler. A non zero + stack size must be specified in PeriodicSmiEnable() for this function to be + used. + + If the stack size passed into PeriodicSmiEnable() was zero, the 0 is returned. + + If this function is not called from within an enabled periodic SMI handler, + then 0 is returned. + + @return The actual time in 100ns units elasped since this function was + called. A value of 0 indicates an unknown amount of time. + +**/ +UINT64 +EFIAPI +PeriodicSmiYield ( + VOID + ) +{ + PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *PeriodicSmiLibraryHandler; + UINTN SetJumpFlag; + + // + // If there is no active periodic SMI handler, then return + // + PeriodicSmiLibraryHandler = GetActivePeriodicSmiLibraryHandler (); + if (PeriodicSmiLibraryHandler == NULL) { + return 0; + } + + // + // If PeriodicSmiYield() is called without an allocated stack, then just return + // immediately with an elapsed time of 0. + // + if (PeriodicSmiLibraryHandler->Stack == NULL) { + return 0; + } + + // + // Set a flag so the next periodic SMI event will resume at where SetJump() + // is called below. + // + PeriodicSmiLibraryHandler->YieldFlag = TRUE; + + // + // Save context in YieldJumpBuffer + // + SetJumpFlag = SetJump (&PeriodicSmiLibraryHandler->YieldJumpBuffer); + if (SetJumpFlag == 0) { + // + // The intial call to SetJump() always returns 0. + // If this is the initial call, then exit the current periodic SMI handler + // + PeriodicSmiExit (); + } + + // + // We get here when a LongJump is performed from PeriodicSmiDispatchFunctionOnCpu() + // to resume a periodic SMI handler that called PeriodicSmiYield() on the + // previous time this periodic SMI handler was dispatched. + // + // Clear the flag so the next periodic SMI dispatch will not resume. + // + PeriodicSmiLibraryHandler->YieldFlag = FALSE; + + // + // Return the amount elapsed time that occured while yielded + // + return PeriodicSmiLibraryHandler->ElapsedTime; +} + +/** + Internal worker function that transfers control to an enabled periodic SMI + handler. If the enabled periodic SMI handler was allocated its own stack, + then this function is called on that allocated stack through the BaseLin + function SwitchStack(). + + @param[in] Context1 Context1 parameter passed into SwitchStack(). + @param[in] Context2 Context2 parameter passed into SwitchStack(). + +**/ +VOID +EFIAPI +PeriodicSmiDispatchFunctionSwitchStack ( + IN VOID *Context1, OPTIONAL + IN VOID *Context2 OPTIONAL + ) +{ + PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *PeriodicSmiLibraryHandler; + + // + // Convert Context1 to PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT * + // + PeriodicSmiLibraryHandler = (PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *)Context1; + + // + // Dispatch the registered handler passing in the context that was registered + // and the amount of time that has elapsed since the previous time this + // periodic SMI handler was dispacthed. + // + PeriodicSmiLibraryHandler->DispatchFunction ( + PeriodicSmiLibraryHandler->Context, + PeriodicSmiLibraryHandler->ElapsedTime + ); + + // + // If this DispatchFunction() returns, then unconditially call PeriodicSmiExit() + // to perform a LongJump() back to PeriodicSmiDispatchFunctionOnCpu(). The + // LongJump() will resume exection on the original stack. + // + PeriodicSmiExit (); +} + +/** + Internal worker function that transfers control to an enabled periodic SMI + handler on the specified logial CPU. This function determines if the periodic + SMI handler yielded and needs to be resumed. It also and switches to an + allocated stack if one was allocated in PeriodicSmiEnable(). + + @param[in] PeriodicSmiLibraryHandler A pointer to the context for the periodic + SMI handler to execute. + +**/ +VOID +EFIAPI +PeriodicSmiDispatchFunctionOnCpu ( + PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *PeriodicSmiLibraryHandler + ) +{ + // + // Save context in DispatchJumpBuffer. The intial call to SetJump() always + // returns 0. If this is the initial call, then either resume from a prior + // call to PeriodicSmiYield() or call the DispatchFunction registerd in + // PeriodicSmiEnable() using an allocated stack if one was specified. + // + if (SetJump (&PeriodicSmiLibraryHandler->DispatchJumpBuffer) != 0) { + return; + } + + // + // Capture the performance counter value just before the periodic SMI handler + // is resumed so the amount of time the periodic SMI handler executes can be + // calculated. + // + PeriodicSmiLibraryHandler->DispatchTotalTime = 0; + PeriodicSmiLibraryHandler->DispatchCheckPointTime = GetPerformanceCounter(); + + if (PeriodicSmiLibraryHandler->YieldFlag) { + // + // Perform a long jump back to the point where the previously dispatched + // function called PeriodicSmiYield(). + // + LongJump (&PeriodicSmiLibraryHandler->YieldJumpBuffer, 1); + } else if (PeriodicSmiLibraryHandler->Stack == NULL) { + // + // If Stack is NULL then call DispatchFunction using current stack passing + // in the context that was registered and the amount of time that has + // elapsed since the previous time this periodic SMI handler was dispacthed. + // + PeriodicSmiLibraryHandler->DispatchFunction ( + PeriodicSmiLibraryHandler->Context, + PeriodicSmiLibraryHandler->ElapsedTime + ); + + // + // If this DispatchFunction() returns, then unconditially call PeriodicSmiExit() + // to perform a LongJump() back to this function. + // + PeriodicSmiExit (); + } else { + // + // If Stack is not NULL then call DispatchFunction switching to the allocated stack + // + SwitchStack ( + PeriodicSmiDispatchFunctionSwitchStack, + PeriodicSmiLibraryHandler, + NULL, + (UINT8 *)PeriodicSmiLibraryHandler->Stack + PeriodicSmiLibraryHandler->StackSize + ); + } + + // + // Must never return + // + ASSERT (FALSE); + CpuDeadLoop(); +} + +/** + Internal worker function that transfers control to an enabled periodic SMI + handler on the specified logial CPU. This worker function is only called + using the SMM Services Table function SmmStartupThisAp() to execute the + periodic SMI handler on a logical CPU that is different than the one that is + running the SMM Foundation. When the periodic SMI handler returns, a lock is + released to notify the CPU that is running the SMM Foundation that the periodic + SMI handler execution has finished its execution. + + @param[in] Buffer A pointer to the context for the periodic SMI handler. + +**/ +VOID +EFIAPI +PeriodicSmiDispatchFunctionWithLock ( + IN OUT VOID *Buffer + ) +{ + PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *PeriodicSmiLibraryHandler; + + // + // Get context + // + PeriodicSmiLibraryHandler = (PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *)Buffer; + + // + // Execute dispatch function on the currently excuting logical CPU + // + PeriodicSmiDispatchFunctionOnCpu (PeriodicSmiLibraryHandler); + + // + // Release the dispatch spin lock + // + ReleaseSpinLock (&PeriodicSmiLibraryHandler->DispatchLock); +} + +/** + Internal worker function that transfers control to a periodic SMI handler that + was enabled using PeriodicSmiEnable(). + + @param[in] DispatchHandle The unique handle assigned to this handler by + SmiHandlerRegister(). + @param[in] Context Points to an optional handler context which was + specified when the handler was registered. + @param[in,out] CommBuffer A pointer to a collection of data in memory that + will be conveyed from a non-SMM environment into + an SMM environment. + @param[in,out] CommBufferSize The size of the CommBuffer. + + @retval EFI_SUCCESS The interrupt was handled and quiesced. + No other handlers should still be called. + @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other + handlers should still be called. + @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other + handlers should still be called. + @retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced. + +**/ +EFI_STATUS +EFIAPI +PeriodicSmiDispatchFunction ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context OPTIONAL, + IN OUT VOID *CommBuffer OPTIONAL, + IN OUT UINTN *CommBufferSize OPTIONAL + ) +{ + PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *PeriodicSmiLibraryHandler; + EFI_SMM_PERIODIC_TIMER_CONTEXT *TimerContext; + EFI_STATUS Status; + + // + // Set the active periodic SMI handler + // + PeriodicSmiLibraryHandler = SetActivePeriodicSmiLibraryHandler (Context); + if (PeriodicSmiLibraryHandler == NULL) { + return EFI_NOT_FOUND; + } + + // + // Retrieve the elapsed time since the last time this periodic SMI handler was called + // + PeriodicSmiLibraryHandler->ElapsedTime = 0; + if (CommBuffer != NULL) { + TimerContext = (EFI_SMM_PERIODIC_TIMER_CONTEXT *)CommBuffer; + PeriodicSmiLibraryHandler->ElapsedTime = TimerContext->ElapsedTime; + } + + // + // Dispatch the periodic SMI handler + // + if ((PeriodicSmiLibraryHandler->Cpu == PERIODIC_SMI_LIBRARY_ANY_CPU) || + (PeriodicSmiLibraryHandler->Cpu == gSmst->CurrentlyExecutingCpu) ) { + // + // Dispatch on the currently execution CPU if the CPU specified in PeriodicSmiEnable() + // was PERIODIC_SMI_LIBARRY_ANY_CPU or the currently executing CPU matches the CPU + // that was specified in PeriodicSmiEnable(). + // + PeriodicSmiDispatchFunctionOnCpu (PeriodicSmiLibraryHandler); + } else { + // + // Acquire spin lock for ths periodic SMI handler. The AP will release the + // spin lock when it is done executing the periodic SMI handler. + // + AcquireSpinLock (&PeriodicSmiLibraryHandler->DispatchLock); + + // + // Execute the periodic SMI handler on the CPU that was specified in + // PeriodicSmiEnable(). + // + Status = gSmst->SmmStartupThisAp ( + PeriodicSmiDispatchFunctionWithLock, + PeriodicSmiLibraryHandler->Cpu, + PeriodicSmiLibraryHandler + ); + if (!EFI_ERROR (Status)) { + // + // Wait for the AP to release the spin lock. + // + while (!AcquireSpinLockOrFail (&PeriodicSmiLibraryHandler->DispatchLock)) { + CpuPause (); + } + } + + // + // Release the spin lock for the periodic SMI handler. + // + ReleaseSpinLock (&PeriodicSmiLibraryHandler->DispatchLock); + } + + // + // Retrieve the active periodic SMI handler in case the entries were reallocated + // when the active periodic SMI handler was dispatched. + // + PeriodicSmiLibraryHandler = GetActivePeriodicSmiLibraryHandler (); + if (PeriodicSmiLibraryHandler != NULL) { + // + // If the active periodic SMI handler was disabled during the current dispatch + // and the periodic SMI handler was allocated a stack when it was enabled, then + // free that stack here. + // + if (PeriodicSmiLibraryHandler->DispatchHandle == NULL) { + if (PeriodicSmiLibraryHandler->Stack != NULL) { + FreePages ( + PeriodicSmiLibraryHandler->Stack, + EFI_SIZE_TO_PAGES (PeriodicSmiLibraryHandler->StackSize) + ); + PeriodicSmiLibraryHandler->Stack = NULL; + } + } + } + + // + // Update state to show that there is no active periodic SMI handler + // + SetActivePeriodicSmiLibraryHandler (NULL); + + return EFI_SUCCESS; +} + +/** + This function enables a periodic SMI handler. + + @param[in,out] DispatchHandle A pointer to the handle associated with the + enabled periodic SMI handler. This is an + optional parameter that may be NULL. If it is + NULL, then the handle will not be returned, + which means that the periodic SMI handler can + never be disabled. + @param[in] DispatchFunction A pointer to a periodic SMI handler function. + @param[in] Context Optional content to pass into DispatchFunction. + @param[in] TickPeriod The requested tick period in 100ns units that + control should be givien to the periodic SMI + handler. Must be one of the supported values + returned by PeriodicSmiSupportedPickPeriod(). + @param[in] Cpu Specifies the CPU that is required to execute + the periodic SMI handler. If Cpu is + PERIODIC_SMI_LIBRARY_ANY_CPU, then the periodic + SMI handler will always be executed on the SMST + CurrentlyExecutingCpu, which may vary across + periodic SMIs. If Cpu is between 0 and the SMST + NumberOfCpus, then the periodic SMI will always + be executed on the requested CPU. + @param[in] StackSize The size, in bytes, of the stack to allocate for + use by the periodic SMI handler. If 0, then the + default stack will be used. + + @retval EFI_INVALID_PARAMETER DispatchFunction is NULL. + @retval EFI_UNSUPPORTED TickPeriod is not a supported tick period. The + supported tick periods can be retrieved using + PeriodicSmiSupportedTickPeriod(). + @retval EFI_INVALID_PARAMETER Cpu is not PERIODIC_SMI_LIBRARY_ANY_CPU or in + the range 0 to SMST NumberOfCpus. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to enable the + periodic SMI handler. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate the + stack speficied by StackSize. + @retval EFI_SUCCESS The periodic SMI handler was enabled. + +**/ +EFI_STATUS +EFIAPI +PeriodicSmiEnable ( + IN OUT EFI_HANDLE *DispatchHandle, OPTIONAL + IN PERIODIC_SMI_LIBRARY_HANDLER DispatchFunction, + IN CONST VOID *Context, OPTIONAL + IN UINT64 TickPeriod, + IN UINTN Cpu, + IN UINTN StackSize + ) +{ + EFI_STATUS Status; + UINTN Index; + PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *PeriodicSmiLibraryHandler; + + // + // Make sure all the input parameters are valid + // + if (DispatchFunction == NULL) { + return EFI_INVALID_PARAMETER; + } + + for (Index = 0; gSmiTickPeriodTable[Index] != 0; Index++) { + if (gSmiTickPeriodTable[Index] == TickPeriod) { + break; + } + } + if (gSmiTickPeriodTable[Index] == 0) { + return EFI_UNSUPPORTED; + } + + if (Cpu != PERIODIC_SMI_LIBRARY_ANY_CPU && Cpu >= gSmst->NumberOfCpus) { + return EFI_INVALID_PARAMETER; + } + + // + // Find a free periodic SMI handler entry + // + PeriodicSmiLibraryHandler = FindFreePeriodicSmiLibraryHandler(); + if (PeriodicSmiLibraryHandler == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Initialize a new periodic SMI handler entry + // + PeriodicSmiLibraryHandler->Signature = PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT_SIGNATURE; + PeriodicSmiLibraryHandler->YieldFlag = FALSE; + PeriodicSmiLibraryHandler->DispatchHandle = NULL; + PeriodicSmiLibraryHandler->DispatchFunction = DispatchFunction; + PeriodicSmiLibraryHandler->Context = (VOID *)Context; + PeriodicSmiLibraryHandler->Cpu = Cpu; + PeriodicSmiLibraryHandler->StackSize = ALIGN_VALUE (StackSize, EFI_PAGE_SIZE); + if (PeriodicSmiLibraryHandler->StackSize > 0) { + PeriodicSmiLibraryHandler->Stack = AllocatePages (EFI_SIZE_TO_PAGES (PeriodicSmiLibraryHandler->StackSize)); + if (PeriodicSmiLibraryHandler->Stack == NULL) { + return EFI_OUT_OF_RESOURCES; + } + ZeroMem (PeriodicSmiLibraryHandler->Stack, PeriodicSmiLibraryHandler->StackSize); + } + InitializeSpinLock (&PeriodicSmiLibraryHandler->DispatchLock); + PeriodicSmiLibraryHandler->PerfomanceCounterRate = GetPerformanceCounterProperties ( + &PeriodicSmiLibraryHandler->PerfomanceCounterStartValue, + &PeriodicSmiLibraryHandler->PerfomanceCounterEndValue + ); + PeriodicSmiLibraryHandler->RegisterContext.Period = TickPeriod; + PeriodicSmiLibraryHandler->RegisterContext.SmiTickInterval = TickPeriod; + Status = gSmmPeriodicTimerDispatch2->Register ( + gSmmPeriodicTimerDispatch2, + PeriodicSmiDispatchFunction, + &PeriodicSmiLibraryHandler->RegisterContext, + &PeriodicSmiLibraryHandler->DispatchHandle + ); + if (EFI_ERROR (Status) || PeriodicSmiLibraryHandler->DispatchHandle == NULL) { + // + // If the registration failed or the handle is invalid, free the stack if one was allocated + // + if (PeriodicSmiLibraryHandler->Stack != NULL) { + FreePages ( + PeriodicSmiLibraryHandler->Stack, + EFI_SIZE_TO_PAGES (PeriodicSmiLibraryHandler->StackSize) + ); + PeriodicSmiLibraryHandler->Stack = NULL; + } + PeriodicSmiLibraryHandler->DispatchHandle = NULL; + return EFI_OUT_OF_RESOURCES; + } + + // + // Return the registered handle if the optional DispatchHandle parameter is not NULL + // + if (DispatchHandle != NULL) { + *DispatchHandle = PeriodicSmiLibraryHandler->DispatchHandle; + } + return EFI_SUCCESS; +} + +/** + This function disables a periodic SMI handler that has been previously + enabled with PeriodicSmiEnable(). + + @param[in] DispatchHandle A handle associated with a previously enabled periodic + SMI handler. This is an optional parameter that may + be NULL. If it is NULL, then the active periodic SMI + handlers is disabled. + + @retval FALSE DispatchHandle is NULL and there is no active periodic SMI handler. + @retval FALSE The periodic SMI handler specified by DispatchHandle has + not been enabled with PeriodicSmiEnable(). + @retval TRUE The periodic SMI handler specified by DispatchHandle has + been disabled. If DispatchHandle is NULL, then the active + periodic SMI handler has been disabled. + +**/ +BOOLEAN +EFIAPI +PeriodicSmiDisable ( + IN EFI_HANDLE DispatchHandle OPTIONAL + ) +{ + PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *PeriodicSmiLibraryHandler; + EFI_STATUS Status; + + // + // Lookup the periodic SMI handler specified by DispatchHandle + // + PeriodicSmiLibraryHandler = LookupPeriodicSmiLibraryHandler (DispatchHandle); + if (PeriodicSmiLibraryHandler == NULL) { + return FALSE; + } + + // + // Unregister the periodic SMI handler from the SMM Periodic Timer Dispatch 2 Protocol + // + Status = gSmmPeriodicTimerDispatch2->UnRegister ( + gSmmPeriodicTimerDispatch2, + PeriodicSmiLibraryHandler->DispatchHandle + ); + if (EFI_ERROR (Status)) { + return FALSE; + } + + // + // If active periodic SMI handler is not the periodic SMI handler being + // disabled, and the periodic SMI handler being disabled was allocated a + // stack when it was enabled, then free the stack. + // + if (PeriodicSmiLibraryHandler != GetActivePeriodicSmiLibraryHandler ()) { + if (PeriodicSmiLibraryHandler->Stack != NULL) { + FreePages ( + PeriodicSmiLibraryHandler->Stack, + EFI_SIZE_TO_PAGES (PeriodicSmiLibraryHandler->StackSize) + ); + PeriodicSmiLibraryHandler->Stack = NULL; + } + } + + // + // Mark the entry for the disabled periodic SMI handler as free + // + PeriodicSmiLibraryHandler->DispatchHandle = NULL; + + return TRUE; +} + +/** + This constructor function caches the pointer to the SMM Periodic Timer + Dispatch 2 Protocol and collects the list SMI tick rates that the hardware + supports. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS. + +**/ +EFI_STATUS +EFIAPI +SmmPeriodicSmiLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + UINT64 *SmiTickInterval; + UINTN Count; + + // + // Locate the SMM Periodic Timer Dispatch 2 Protocol + // + Status = gSmst->SmmLocateProtocol ( + &gEfiSmmPeriodicTimerDispatch2ProtocolGuid, + NULL, + (VOID **)&gSmmPeriodicTimerDispatch2 + ); + ASSERT_EFI_ERROR (Status); + ASSERT (gSmmPeriodicTimerDispatch2 != NULL); + + // + // Count the number of periodic SMI tick intervals that the SMM Periodic Timer + // Dipatch 2 Protocol supports. + // + SmiTickInterval = NULL; + Count = 0; + do { + Status = gSmmPeriodicTimerDispatch2->GetNextShorterInterval ( + gSmmPeriodicTimerDispatch2, + &SmiTickInterval + ); + Count++; + } while (SmiTickInterval != NULL); + + // + // Allocate a buffer for the table of supported periodic SMI tick periods. + // + gSmiTickPeriodTable = AllocateZeroPool (Count * sizeof (UINT64)); + ASSERT (gSmiTickPeriodTable != NULL); + + // + // Fill in the table of supported periodic SMI tick periods. + // + SmiTickInterval = NULL; + Count = 0; + do { + gSmiTickPeriodTable[Count] = 0; + Status = gSmmPeriodicTimerDispatch2->GetNextShorterInterval ( + gSmmPeriodicTimerDispatch2, + &SmiTickInterval + ); + if (SmiTickInterval != NULL) { + gSmiTickPeriodTable[Count] = *SmiTickInterval; + } + Count++; + } while (SmiTickInterval != NULL); + + // + // Allocate buffer for initial set of periodic SMI handlers + // + FindFreePeriodicSmiLibraryHandler (); + + return EFI_SUCCESS; +} + +/** + The constructor function caches the pointer to the SMM Periodic Timer Dispatch 2 + Protocol and collects the list SMI tick rates that the hardware supports. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS. + +**/ +EFI_STATUS +EFIAPI +SmmPeriodicSmiLibDestructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + UINTN Index; + + // + // Free the table of supported periodic SMI tick rates + // + if (gSmiTickPeriodTable != NULL) { + FreePool (gSmiTickPeriodTable); + } + + // + // Disable all periodic SMI handlers + // + for (Index = 0; Index < gNumberOfPeriodicSmiLibraryHandlers; Index++) { + PeriodicSmiDisable (gPeriodicSmiLibraryHandlers[Index].DispatchHandle); + } + + // + // Free all the periodic SMI handler entries + // + if (gPeriodicSmiLibraryHandlers != NULL) { + FreePool (gPeriodicSmiLibraryHandlers); + } + + return EFI_SUCCESS; +} diff --git a/MdePkg/Library/SmmPeriodicSmiLib/SmmPeriodicSmiLib.inf b/MdePkg/Library/SmmPeriodicSmiLib/SmmPeriodicSmiLib.inf new file mode 100644 index 0000000000..2acb59afaf --- /dev/null +++ b/MdePkg/Library/SmmPeriodicSmiLib/SmmPeriodicSmiLib.inf @@ -0,0 +1,50 @@ +## @file +# SMM Periodic SMI Library. +# +# Copyright (c) 2011, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = SmmPeriodicSmiLib + FILE_GUID = AED5F3FB-4CFF-4b60-9E43-1541B55C8267 + MODULE_TYPE = DXE_SMM_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = SmmPeriodicSmiLib|DXE_SMM_DRIVER + PI_SPECIFICATION_VERSION = 0x0001000A + CONSTRUCTOR = SmmPeriodicSmiLibConstructor + DESTRUCTOR = SmmPeriodicSmiLibDestructor + +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + SmmPeriodicSmiLib.c + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + SynchronizationLib + DebugLib + TimerLib + MemoryAllocationLib + SmmServicesTableLib + +[Protocols] + gEfiSmmPeriodicTimerDispatch2ProtocolGuid + +[Depex] + gEfiSmmPeriodicTimerDispatch2ProtocolGuid -- cgit v1.2.3