]> git.proxmox.com Git - mirror_edk2.git/commitdiff
Add SmmPeriodicSmiLib to MdePkg.
authormdkinney <mdkinney@6f19259b-4bc3-4df7-8a09-765794883524>
Fri, 11 Feb 2011 00:09:16 +0000 (00:09 +0000)
committermdkinney <mdkinney@6f19259b-4bc3-4df7-8a09-765794883524>
Fri, 11 Feb 2011 00:09:16 +0000 (00:09 +0000)
git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@11302 6f19259b-4bc3-4df7-8a09-765794883524

MdePkg/Include/Library/SmmPeriodicSmiLib.h [new file with mode: 0644]
MdePkg/Library/SmmPeriodicSmiLib/SmmPeriodicSmiLib.c [new file with mode: 0644]
MdePkg/Library/SmmPeriodicSmiLib/SmmPeriodicSmiLib.inf [new file with mode: 0644]
MdePkg/MdePkg.dec
MdePkg/MdePkg.dsc

diff --git a/MdePkg/Include/Library/SmmPeriodicSmiLib.h b/MdePkg/Include/Library/SmmPeriodicSmiLib.h
new file mode 100644 (file)
index 0000000..efdff6f
--- /dev/null
@@ -0,0 +1,184 @@
+/** @file\r
+  Provides services to enable and disable periodic SMI handlers.\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
+#ifndef __PERIODIC_SMI_LIB_H__\r
+#define __PERIODIC_SMI_LIB_H__\r
+\r
+#define PERIODIC_SMI_LIBRARY_ANY_CPU  0xffffffff\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
+  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
+/**\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
+/**\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
+/**\r
+  This function is a prototype for a periodic SMI handler function \r
+  that may be enabled with PeriodicSmiEnable() and disabled with\r
+  PeriodicSmiDisable().\r
+\r
+  @param[in] Context      Content registered with PeriodicSmiEnable().\r
+  @param[in] ElapsedTime  The actual time in 100ns units elasped since \r
+                          this function was called.  A value of 0 indicates \r
+                          an unknown amount of time.\r
+                            \r
+**/\r
+typedef\r
+VOID\r
+(EFIAPI *PERIODIC_SMI_LIBRARY_HANDLER) (\r
+  IN CONST VOID  *Context OPTIONAL,\r
+  IN UINT64      ElapsedTime\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
+/**\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
+#endif\r
diff --git a/MdePkg/Library/SmmPeriodicSmiLib/SmmPeriodicSmiLib.c b/MdePkg/Library/SmmPeriodicSmiLib/SmmPeriodicSmiLib.c
new file mode 100644 (file)
index 0000000..a941d75
--- /dev/null
@@ -0,0 +1,1165 @@
+/** @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
diff --git a/MdePkg/Library/SmmPeriodicSmiLib/SmmPeriodicSmiLib.inf b/MdePkg/Library/SmmPeriodicSmiLib/SmmPeriodicSmiLib.inf
new file mode 100644 (file)
index 0000000..2acb59a
--- /dev/null
@@ -0,0 +1,50 @@
+## @file\r
+# SMM Periodic SMI Library.\r
+#\r
+# Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>\r
+#\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
+#  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
+\r
+[Defines]\r
+  INF_VERSION                    = 0x00010005\r
+  BASE_NAME                      = SmmPeriodicSmiLib\r
+  FILE_GUID                      = AED5F3FB-4CFF-4b60-9E43-1541B55C8267\r
+  MODULE_TYPE                    = DXE_SMM_DRIVER\r
+  VERSION_STRING                 = 1.0\r
+  LIBRARY_CLASS                  = SmmPeriodicSmiLib|DXE_SMM_DRIVER\r
+  PI_SPECIFICATION_VERSION       = 0x0001000A\r
+  CONSTRUCTOR                    = SmmPeriodicSmiLibConstructor\r
+  DESTRUCTOR                     = SmmPeriodicSmiLibDestructor\r
+  \r
+#\r
+#  VALID_ARCHITECTURES           = IA32 X64\r
+#\r
+\r
+[Sources]\r
+  SmmPeriodicSmiLib.c\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+\r
+[LibraryClasses]\r
+  BaseLib\r
+  BaseMemoryLib\r
+  SynchronizationLib\r
+  DebugLib\r
+  TimerLib\r
+  MemoryAllocationLib\r
+  SmmServicesTableLib\r
+  \r
+[Protocols]\r
+  gEfiSmmPeriodicTimerDispatch2ProtocolGuid\r
+  \r
+[Depex]\r
+  gEfiSmmPeriodicTimerDispatch2ProtocolGuid\r
index cdcab950ca645218af7f71054c346f2fddc8bfe7..60c8a3cc83a76b9b24ec6614e7976d085486687a 100644 (file)
@@ -5,7 +5,7 @@
 # It also provides the definitions(including PPIs/PROTOCOLs/GUIDs) of\r
 # EFI1.10/UEFI2.3/PI1.2 and some Industry Standards.\r
 #\r
-# Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>\r
+# Copyright (c) 2007 - 2011, Intel Corporation. All rights reserved.<BR>\r
 # Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>\r
 #\r
 # This program and the accompanying materials are licensed and made available under\r
   #                  Only available to SMM/DXE Combined and SMM module types.\r
   SmmServicesTableLib|Include/Library/SmmServicesTableLib.h\r
 \r
+  ##  @libraryclass  Provides services to enable/disable periodic SMI handlers.\r
+  #\r
+  SmmPeriodicSmiLib|Include/Library/SmmPeriodicSmiLib.h\r
+  \r
 [LibraryClasses.IPF]\r
   ##  @libraryclass  The SAL Library provides a service to make a SAL CALL.\r
   SalLib|Include/Library/SalLib.h\r
index 04242b2b2eef0471656ed7fe6d9b60efc59ed118..9ca57b840da80bcc2815747efff7a89259846fee 100644 (file)
@@ -1,7 +1,7 @@
 ## @file\r
 # EFI/PI MdePkg Package\r
 #\r
-# Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>\r
+# Copyright (c) 2007 - 2011, Intel Corporation. All rights reserved.<BR>\r
 # Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>\r
 #\r
 #    This program and the accompanying materials\r
   MdePkg/Library/SmmPciLibPciRootBridgeIo/SmmPciLibPciRootBridgeIo.inf\r
   MdePkg/Library/SmmServicesTableLib/SmmServicesTableLib.inf\r
   MdePkg/Library/SmmMemoryAllocationLib/SmmMemoryAllocationLib.inf\r
+  MdePkg/Library/SmmPeriodicSmiLib/SmmPeriodicSmiLib.inf  \r
 \r
 [Components.IPF]\r
   MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf\r
   MdePkg/Library/UefiPalLib/UefiPalLib.inf\r
   MdePkg/Library/UefiSalLib/UefiSalLib.inf\r
 \r
-\r
 [Components.EBC]\r
   MdePkg/Library/UefiRuntimeLib/UefiRuntimeLib.inf\r
 \r