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