summaryrefslogtreecommitdiff
path: root/Core/MdePkg/Library/SmmPeriodicSmiLib/SmmPeriodicSmiLib.c
diff options
context:
space:
mode:
Diffstat (limited to 'Core/MdePkg/Library/SmmPeriodicSmiLib/SmmPeriodicSmiLib.c')
-rw-r--r--Core/MdePkg/Library/SmmPeriodicSmiLib/SmmPeriodicSmiLib.c1183
1 files changed, 1183 insertions, 0 deletions
diff --git a/Core/MdePkg/Library/SmmPeriodicSmiLib/SmmPeriodicSmiLib.c b/Core/MdePkg/Library/SmmPeriodicSmiLib/SmmPeriodicSmiLib.c
new file mode 100644
index 0000000000..b6db31766c
--- /dev/null
+++ b/Core/MdePkg/Library/SmmPeriodicSmiLib/SmmPeriodicSmiLib.c
@@ -0,0 +1,1183 @@
+/** @file
+ SMM Periodic SMI Library.
+
+ Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>
+ 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 <PiSmm.h>
+
+#include <Protocol/SmmPeriodicTimerDispatch2.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/SynchronizationLib.h>
+#include <Library/DebugLib.h>
+#include <Library/TimerLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/SmmServicesTableLib.h>
+
+#include <Library/SmmPeriodicSmiLib.h>
+
+///
+/// Define the number of periodic SMI handler entries that should be allocated to the list
+/// of free periodic SMI handlers when the list of free periodic SMI handlers is empty.
+///
+#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 link entry to be inserted to the list of periodic SMI handlers.
+ ///
+ LIST_ENTRY Link;
+ ///
+ /// 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 \
+ )
+
+/**
+ Macro that returns a pointer to a PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT
+ structure based on a pointer to a Link field.
+
+**/
+#define PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT_FROM_LINK(a) \
+ CR ( \
+ a, \
+ PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT, \
+ Link, \
+ 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;
+
+///
+/// Linked list of free periodic SMI handlers that this library can use.
+///
+LIST_ENTRY gFreePeriodicSmiLibraryHandlers =
+ INITIALIZE_LIST_HEAD_VARIABLE (gFreePeriodicSmiLibraryHandlers);
+
+///
+/// Linked list of periodic SMI handlers that this library is currently managing.
+///
+LIST_ENTRY gPeriodicSmiLibraryHandlers =
+ INITIALIZE_LIST_HEAD_VARIABLE (gPeriodicSmiLibraryHandlers);
+
+///
+/// Pointer to the periodic SMI handler that is currently being executed.
+/// Is set to NULL if no periodic SMI handler is currently being executed.
+///
+PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *gActivePeriodicSmiLibraryHandler = NULL;
+
+/**
+ 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
+ )
+{
+ return gActivePeriodicSmiLibraryHandler;
+}
+
+/**
+ 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
+ )
+{
+ LIST_ENTRY *Link;
+ PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *PeriodicSmiLibraryHandler;
+
+ //
+ // 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 ( Link = GetFirstNode (&gPeriodicSmiLibraryHandlers)
+ ; !IsNull (&gPeriodicSmiLibraryHandlers, Link)
+ ; Link = GetNextNode (&gPeriodicSmiLibraryHandlers, Link)
+ ) {
+ PeriodicSmiLibraryHandler = PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT_FROM_LINK (Link);
+
+ if (PeriodicSmiLibraryHandler->DispatchHandle == DispatchHandle) {
+ return PeriodicSmiLibraryHandler;
+ }
+ }
+
+ //
+ // 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
+ )
+{
+ if (Context == NULL) {
+ gActivePeriodicSmiLibraryHandler = NULL;
+ } else {
+ gActivePeriodicSmiLibraryHandler = PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT_FROM_REGISTER_CONTEXT (Context);
+ }
+ return gActivePeriodicSmiLibraryHandler;
+}
+
+/**
+ Internal worker function that moves the specified periodic SMI handler from the
+ list of managed periodic SMI handlers to the list of free periodic SMI handlers.
+
+ @param[in] PeriodicSmiLibraryHandler Pointer to the periodic SMI handler to be reclaimed.
+**/
+VOID
+ReclaimPeriodicSmiLibraryHandler (
+ PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *PeriodicSmiLibraryHandler
+ )
+{
+ ASSERT (PeriodicSmiLibraryHandler->DispatchHandle == NULL);
+ if (PeriodicSmiLibraryHandler->Stack != NULL) {
+ FreePages (
+ PeriodicSmiLibraryHandler->Stack,
+ EFI_SIZE_TO_PAGES (PeriodicSmiLibraryHandler->StackSize)
+ );
+ PeriodicSmiLibraryHandler->Stack = NULL;
+ }
+ RemoveEntryList (&PeriodicSmiLibraryHandler->Link);
+ InsertHeadList (&gFreePeriodicSmiLibraryHandlers, &PeriodicSmiLibraryHandler->Link);
+}
+
+/**
+ Add the additional entries to the list of free periodic SMI handlers.
+ The function is assumed to be called only when the list of free periodic SMI
+ handlers is empty.
+
+ @retval TRUE The additional entries were added.
+ @retval FALSE There was no available resource for the additional entries.
+**/
+BOOLEAN
+EnlargeFreePeriodicSmiLibraryHandlerList (
+ VOID
+ )
+{
+ UINTN Index;
+ PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *PeriodicSmiLibraryHandler;
+
+ //
+ // Add the entries to the list
+ //
+ for (Index = 0; Index < PERIODIC_SMI_LIBRARY_ALLOCATE_SIZE; Index++) {
+ PeriodicSmiLibraryHandler = AllocatePool (sizeof (PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT));
+ if (PeriodicSmiLibraryHandler == NULL) {
+ break;
+ }
+ PeriodicSmiLibraryHandler->Signature = PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT_SIGNATURE;
+ InsertHeadList (&gFreePeriodicSmiLibraryHandlers, &PeriodicSmiLibraryHandler->Link);
+ }
+
+ return (BOOLEAN) (Index > 0);
+}
+
+/**
+ 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
+ )
+{
+ PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *PeriodicSmiLibraryHandler;
+
+ if (IsListEmpty (&gFreePeriodicSmiLibraryHandlers)) {
+ if (!EnlargeFreePeriodicSmiLibraryHandlerList ()) {
+ return NULL;
+ }
+ }
+
+ //
+ // Get one from the list of free periodic SMI handlers.
+ //
+ PeriodicSmiLibraryHandler = PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT_FROM_LINK (
+ GetFirstNode (&gFreePeriodicSmiLibraryHandlers)
+ );
+ RemoveEntryList (&PeriodicSmiLibraryHandler->Link);
+ InsertTailList (&gPeriodicSmiLibraryHandlers, &PeriodicSmiLibraryHandler->Link);
+
+ return PeriodicSmiLibraryHandler;
+}
+
+/**
+ 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, out] 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);
+ }
+
+ //
+ // Reclaim the active periodic SMI handler if it was disabled during the current dispatch.
+ //
+ if (PeriodicSmiLibraryHandler->DispatchHandle == NULL) {
+ ReclaimPeriodicSmiLibraryHandler (PeriodicSmiLibraryHandler);
+ }
+
+ //
+ // 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->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);
+ } else {
+ PeriodicSmiLibraryHandler->Stack = NULL;
+ }
+ 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;
+ ReclaimPeriodicSmiLibraryHandler (PeriodicSmiLibraryHandler);
+ 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;
+ }
+
+ //
+ // Mark the entry for the disabled periodic SMI handler as free, and
+ // call ReclaimPeriodicSmiLibraryHandler to move it to the list of free
+ // periodic SMI handlers.
+ //
+ PeriodicSmiLibraryHandler->DispatchHandle = NULL;
+ if (PeriodicSmiLibraryHandler != GetActivePeriodicSmiLibraryHandler ()) {
+ ReclaimPeriodicSmiLibraryHandler (PeriodicSmiLibraryHandler);
+ }
+
+ 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
+ //
+ EnlargeFreePeriodicSmiLibraryHandlerList ();
+
+ 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
+ )
+{
+ LIST_ENTRY *Link;
+ PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *PeriodicSmiLibraryHandler;
+
+ //
+ // Free the table of supported periodic SMI tick rates
+ //
+ if (gSmiTickPeriodTable != NULL) {
+ FreePool (gSmiTickPeriodTable);
+ }
+
+ //
+ // Disable all periodic SMI handlers
+ //
+ for (Link = GetFirstNode (&gPeriodicSmiLibraryHandlers); !IsNull (&gPeriodicSmiLibraryHandlers, Link);) {
+ PeriodicSmiLibraryHandler = PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT_FROM_LINK (Link);
+ Link = GetNextNode (&gPeriodicSmiLibraryHandlers, Link);
+ PeriodicSmiDisable (PeriodicSmiLibraryHandler->DispatchHandle);
+ }
+
+ //
+ // Free all the periodic SMI handler entries
+ //
+ for (Link = GetFirstNode (&gFreePeriodicSmiLibraryHandlers); !IsNull (&gFreePeriodicSmiLibraryHandlers, Link);) {
+ PeriodicSmiLibraryHandler = PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT_FROM_LINK (Link);
+ Link = RemoveEntryList (Link);
+ FreePool (PeriodicSmiLibraryHandler);
+ }
+
+ return EFI_SUCCESS;
+}