Home | History | Annotate | Download | only in SmmPeriodicSmiLib
      1 /** @file
      2   SMM Periodic SMI Library.
      3 
      4   Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>
      5   This program and the accompanying materials
      6   are licensed and made available under the terms and conditions of the BSD License
      7   which accompanies this distribution.  The full text of the license may be found at
      8   http://opensource.org/licenses/bsd-license.php.
      9 
     10   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     11   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     12 
     13 **/
     14 
     15 #include <PiSmm.h>
     16 
     17 #include <Protocol/SmmPeriodicTimerDispatch2.h>
     18 
     19 #include <Library/BaseLib.h>
     20 #include <Library/BaseMemoryLib.h>
     21 #include <Library/SynchronizationLib.h>
     22 #include <Library/DebugLib.h>
     23 #include <Library/TimerLib.h>
     24 #include <Library/MemoryAllocationLib.h>
     25 #include <Library/SmmServicesTableLib.h>
     26 
     27 #include <Library/SmmPeriodicSmiLib.h>
     28 
     29 ///
     30 /// Define the number of periodic SMI handler entries that should be allocated to the list
     31 /// of free periodic SMI handlers when the list of free periodic SMI handlers is empty.
     32 ///
     33 #define PERIODIC_SMI_LIBRARY_ALLOCATE_SIZE  0x08
     34 
     35 ///
     36 /// Signature for a PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT structure
     37 ///
     38 #define PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT_SIGNATURE  SIGNATURE_32 ('P', 'S', 'M', 'I')
     39 
     40 ///
     41 /// Structure that contains state information for an enabled periodic SMI handler
     42 ///
     43 typedef struct {
     44   ///
     45   /// Signature value that must be set to PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT_SIGNATURE
     46   ///
     47   UINT32                                   Signature;
     48   ///
     49   /// The link entry to be inserted to the list of periodic SMI handlers.
     50   ///
     51   LIST_ENTRY                               Link;
     52   ///
     53   /// The dispatch function to called to invoke an enabled periodic SMI handler.
     54   ///
     55   PERIODIC_SMI_LIBRARY_HANDLER             DispatchFunction;
     56   ///
     57   /// The context to pass into DispatchFunction
     58   ///
     59   VOID                                     *Context;
     60   ///
     61   /// The tick period in 100 ns units that DispatchFunction should be called.
     62   ///
     63   UINT64                                   TickPeriod;
     64   ///
     65   /// The Cpu number that is required to execute DispatchFunction.  If Cpu is
     66   /// set to PERIODIC_SMI_LIBRARY_ANY_CPU, then DispatchFunction may be executed
     67   /// on any CPU.
     68   ///
     69   UINTN                                    Cpu;
     70   ///
     71   /// The size, in bytes, of the stack allocated for a periodic SMI handler.
     72   /// This value must be a multiple of EFI_PAGE_SIZE.
     73   ///
     74   UINTN                                    StackSize;
     75   ///
     76   /// A pointer to the stack allocated using AllocatePages().  This field will
     77   /// be NULL if StackSize is 0.
     78   ///
     79   VOID                                     *Stack;
     80   ///
     81   /// Spin lock used to wait for an AP to complete the execution of a periodic SMI handler
     82   ///
     83   SPIN_LOCK                                DispatchLock;
     84   ///
     85   /// The rate in Hz of the performance counter that is used to measure the
     86   /// amount of time that a periodic SMI handler executes.
     87   ///
     88   UINT64                                   PerfomanceCounterRate;
     89   ///
     90   /// The start count value of the performance counter that is used to measure
     91   /// the amount of time that a periodic SMI handler executes.
     92   ///
     93   UINT64                                   PerfomanceCounterStartValue;
     94   ///
     95   /// The end count value of the performance counter that is used to measure
     96   /// the amount of time that a periodic SMI handler executes.
     97   ///
     98   UINT64                                   PerfomanceCounterEndValue;
     99   ///
    100   /// The context record passed into the Register() function of the SMM Periodic
    101   /// Timer Dispatch Protocol when a periodic SMI handler is enabled.
    102   ///
    103   EFI_SMM_PERIODIC_TIMER_REGISTER_CONTEXT  RegisterContext;
    104   ///
    105   /// The handle returned from the Register() function of the SMM Periodic
    106   /// Timer Dispatch Protocol when a periodic SMI handler is enabled.
    107   ///
    108   EFI_HANDLE                               DispatchHandle;
    109   ///
    110   /// The total number of performance counter ticks that the periodic SMI handler
    111   /// has been executing in its current invocation.
    112   ///
    113   UINT64                                   DispatchTotalTime;
    114   ///
    115   /// The performance counter value that was captured the last time that the
    116   /// periodic SMI handler called PeriodcSmiExecutionTime().  This allows the
    117   /// time value returned by PeriodcSmiExecutionTime() to be accurate even when
    118   /// the performance counter rolls over.
    119   ///
    120   UINT64                                   DispatchCheckPointTime;
    121   ///
    122   /// Buffer used to save the context when control is transfer from this library
    123   /// to an enabled periodic SMI handler.  This saved context is used when the
    124   /// periodic SMI handler exits or yields.
    125   ///
    126   BASE_LIBRARY_JUMP_BUFFER                 DispatchJumpBuffer;
    127   ///
    128   /// Flag that is set to TRUE when a periodic SMI handler requests to yield
    129   /// using PeriodicSmiYield().  When this flag IS TRUE, YieldJumpBuffer is
    130   /// valid.  When this flag is FALSE, YieldJumpBuffer is not valid.
    131   ///
    132   BOOLEAN                                  YieldFlag;
    133   ///
    134   /// Buffer used to save the context when a periodic SMI handler requests to
    135   /// yield using PeriodicSmiYield().  This context is used to resume the
    136   /// execution of a periodic SMI handler the next time control is transferd
    137   /// to the periodic SMI handler that yielded.
    138   ///
    139   BASE_LIBRARY_JUMP_BUFFER                 YieldJumpBuffer;
    140   ///
    141   /// The amount of time, in 100 ns units, that have elapsed since the last
    142   /// time the periodic SMI handler was invoked.
    143   ///
    144   UINT64                                   ElapsedTime;
    145 } PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT;
    146 
    147 /**
    148  Macro that returns a pointer to a PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT
    149  structure based on a pointer to a RegisterContext field.
    150 
    151 **/
    152 #define PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT_FROM_REGISTER_CONTEXT(a) \
    153   CR (                                                                \
    154     a,                                                                \
    155     PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT,                             \
    156     RegisterContext,                                                  \
    157     PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT_SIGNATURE                    \
    158     )
    159 
    160 /**
    161  Macro that returns a pointer to a PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT
    162  structure based on a pointer to a Link field.
    163 
    164 **/
    165 #define PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT_FROM_LINK(a)             \
    166   CR (                                                                \
    167     a,                                                                \
    168     PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT,                             \
    169     Link,                                                             \
    170     PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT_SIGNATURE                    \
    171     )
    172 
    173 ///
    174 /// Pointer to the SMM Periodic Timer Disatch Protocol that was located in the constuctor.
    175 ///
    176 EFI_SMM_PERIODIC_TIMER_DISPATCH2_PROTOCOL  *gSmmPeriodicTimerDispatch2           = NULL;
    177 
    178 ///
    179 /// Pointer to a table of supported periodic SMI tick periods in 100 ns units
    180 /// sorted from largest to smallest terminated by a tick period value of 0.
    181 /// This table is allocated using AllocatePool() in the constructor and filled
    182 /// in based on the values returned from the SMM Periodic Timer Dispatch 2 Protocol
    183 /// function GetNextShorterInterval().
    184 ///
    185 UINT64                                     *gSmiTickPeriodTable                  = NULL;
    186 
    187 ///
    188 /// Linked list of free periodic SMI handlers that this library can use.
    189 ///
    190 LIST_ENTRY                                 gFreePeriodicSmiLibraryHandlers       =
    191                                            INITIALIZE_LIST_HEAD_VARIABLE (gFreePeriodicSmiLibraryHandlers);
    192 
    193 ///
    194 /// Linked list of periodic SMI handlers that this library is currently managing.
    195 ///
    196 LIST_ENTRY                                 gPeriodicSmiLibraryHandlers           =
    197                                            INITIALIZE_LIST_HEAD_VARIABLE (gPeriodicSmiLibraryHandlers);
    198 
    199 ///
    200 /// Pointer to the periodic SMI handler that is currently being executed.
    201 /// Is set to NULL if no periodic SMI handler is currently being executed.
    202 ///
    203 PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT       *gActivePeriodicSmiLibraryHandler     = NULL;
    204 
    205 /**
    206   Internal worker function that returns a pointer to the
    207   PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT structure associated with the periodic
    208   SMI handler that is currently being executed.  If a periodic SMI handler is
    209   not currently being executed, the NULL is returned.
    210 
    211   @retval  NULL   A periodic SMI handler is not currently being executed.
    212   @retval  other  Pointer to the PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT
    213                   associated with the active periodic SMI handler.
    214 
    215 **/
    216 PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *
    217 GetActivePeriodicSmiLibraryHandler (
    218   VOID
    219   )
    220 {
    221   return gActivePeriodicSmiLibraryHandler;
    222 }
    223 
    224 /**
    225   Internal worker function that returns a pointer to the
    226   PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT structure associated with the
    227   DispatchHandle that was returned when the periodic SMI handler was enabled
    228   with PeriodicSmiEnable().  If DispatchHandle is NULL, then the active
    229   periodic SMI handler is returned.  If DispatchHandle is NULL and there is
    230   no active periodic SMI handler, then NULL is returned.
    231 
    232   @param[in] DispatchHandle  DispatchHandle that was returned when the periodic
    233                              SMI handler was enabled with PeriodicSmiEnable().
    234                              This is an optional parameter that may be NULL.
    235                              If this parameter is NULL, then the active periodic
    236                              SMI handler is returned.
    237 
    238   @retval  NULL   DispatchHandle is NULL and there is no active periodic SMI
    239                   handler.
    240   @retval  NULL   DispatchHandle does not match any of the periodic SMI handlers
    241                   that have been enabled with PeriodicSmiEnable().
    242   @retval  other  Pointer to the PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT
    243                   associated with the DispatchHandle.
    244 
    245 **/
    246 PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *
    247 LookupPeriodicSmiLibraryHandler (
    248   IN EFI_HANDLE                         DispatchHandle    OPTIONAL
    249   )
    250 {
    251   LIST_ENTRY                            *Link;
    252   PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT  *PeriodicSmiLibraryHandler;
    253 
    254   //
    255   // If DispatchHandle is NULL, then return the active periodic SMI handler
    256   //
    257   if (DispatchHandle == NULL) {
    258     return GetActivePeriodicSmiLibraryHandler ();
    259   }
    260 
    261   //
    262   // Search the periodic SMI handler entries for a a matching DispatchHandle
    263   //
    264   for ( Link = GetFirstNode (&gPeriodicSmiLibraryHandlers)
    265       ; !IsNull (&gPeriodicSmiLibraryHandlers, Link)
    266       ; Link = GetNextNode (&gPeriodicSmiLibraryHandlers, Link)
    267       ) {
    268     PeriodicSmiLibraryHandler = PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT_FROM_LINK (Link);
    269 
    270     if (PeriodicSmiLibraryHandler->DispatchHandle == DispatchHandle) {
    271       return PeriodicSmiLibraryHandler;
    272     }
    273   }
    274 
    275   //
    276   // No entries match DispatchHandle, so return NULL
    277   //
    278   return NULL;
    279 }
    280 
    281 /**
    282   Internal worker function that sets that active periodic SMI handler based on
    283   the Context used when the periodic SMI handler was registered with the
    284   SMM Periodic Timer Dispatch 2 Protocol.  If Context is NULL, then the
    285   state is updated to show that there is not active periodic SMI handler.
    286   A pointer to the active PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT structure
    287   is returned.
    288 
    289   @retval  NULL   Context is NULL.
    290   @retval  other  Pointer to the PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT
    291                   associated with Context.
    292 
    293 **/
    294 PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *
    295 SetActivePeriodicSmiLibraryHandler (
    296   IN CONST VOID  *Context  OPTIONAL
    297   )
    298 {
    299   if (Context == NULL) {
    300     gActivePeriodicSmiLibraryHandler = NULL;
    301   } else {
    302     gActivePeriodicSmiLibraryHandler = PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT_FROM_REGISTER_CONTEXT (Context);
    303   }
    304   return gActivePeriodicSmiLibraryHandler;
    305 }
    306 
    307 /**
    308   Internal worker function that moves the specified periodic SMI handler from the
    309   list of managed periodic SMI handlers to the list of free periodic SMI handlers.
    310 
    311   @param[in] PeriodicSmiLibraryHandler  Pointer to the periodic SMI handler to be reclaimed.
    312 **/
    313 VOID
    314 ReclaimPeriodicSmiLibraryHandler (
    315   PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT     *PeriodicSmiLibraryHandler
    316   )
    317 {
    318   ASSERT (PeriodicSmiLibraryHandler->DispatchHandle == NULL);
    319   if (PeriodicSmiLibraryHandler->Stack != NULL) {
    320     FreePages (
    321       PeriodicSmiLibraryHandler->Stack,
    322       EFI_SIZE_TO_PAGES (PeriodicSmiLibraryHandler->StackSize)
    323       );
    324     PeriodicSmiLibraryHandler->Stack = NULL;
    325   }
    326   RemoveEntryList (&PeriodicSmiLibraryHandler->Link);
    327   InsertHeadList (&gFreePeriodicSmiLibraryHandlers, &PeriodicSmiLibraryHandler->Link);
    328 }
    329 
    330 /**
    331   Add the additional entries to the list of free periodic SMI handlers.
    332   The function is assumed to be called only when the list of free periodic SMI
    333   handlers is empty.
    334 
    335   @retval TRUE  The additional entries were added.
    336   @retval FALSE There was no available resource for the additional entries.
    337 **/
    338 BOOLEAN
    339 EnlargeFreePeriodicSmiLibraryHandlerList (
    340   VOID
    341   )
    342 {
    343   UINTN                                 Index;
    344   PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT  *PeriodicSmiLibraryHandler;
    345 
    346   //
    347   // Add the entries to the list
    348   //
    349   for (Index = 0; Index < PERIODIC_SMI_LIBRARY_ALLOCATE_SIZE; Index++) {
    350     PeriodicSmiLibraryHandler = AllocatePool (sizeof (PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT));
    351     if (PeriodicSmiLibraryHandler == NULL) {
    352       break;
    353     }
    354     PeriodicSmiLibraryHandler->Signature = PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT_SIGNATURE;
    355     InsertHeadList (&gFreePeriodicSmiLibraryHandlers, &PeriodicSmiLibraryHandler->Link);
    356   }
    357 
    358   return (BOOLEAN) (Index > 0);
    359 }
    360 
    361 /**
    362   Internal worker function that returns a free entry for a new periodic
    363   SMI handler.  If no free entries are available, then additional
    364   entries are allocated.
    365 
    366   @retval  NULL   There are not enough resources available to to allocate a free entry.
    367   @retval  other  Pointer to a free PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT structure.
    368 
    369 **/
    370 PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *
    371 FindFreePeriodicSmiLibraryHandler (
    372   VOID
    373   )
    374 {
    375   PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT  *PeriodicSmiLibraryHandler;
    376 
    377   if (IsListEmpty (&gFreePeriodicSmiLibraryHandlers)) {
    378     if (!EnlargeFreePeriodicSmiLibraryHandlerList ()) {
    379       return NULL;
    380     }
    381   }
    382 
    383   //
    384   // Get one from the list of free periodic SMI handlers.
    385   //
    386   PeriodicSmiLibraryHandler = PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT_FROM_LINK (
    387                                 GetFirstNode (&gFreePeriodicSmiLibraryHandlers)
    388                                 );
    389   RemoveEntryList (&PeriodicSmiLibraryHandler->Link);
    390   InsertTailList (&gPeriodicSmiLibraryHandlers, &PeriodicSmiLibraryHandler->Link);
    391 
    392   return PeriodicSmiLibraryHandler;
    393 }
    394 
    395 /**
    396   This function returns a pointer to a table of supported periodic
    397   SMI tick periods in 100 ns units sorted from largest to smallest.
    398   The table contains a array of UINT64 values terminated by a tick
    399   period value of 0.  The returned table must be treated as read-only
    400   data and must not be freed.
    401 
    402   @return  A pointer to a table of UINT64 tick period values in
    403            100ns units sorted from largest to smallest terminated
    404            by a tick period of 0.
    405 
    406 **/
    407 UINT64 *
    408 EFIAPI
    409 PeriodicSmiSupportedTickPeriod (
    410   VOID
    411   )
    412 {
    413   //
    414   // Return the table allocated and populated by SmmPeriodicSmiLibConstructor()
    415   //
    416   return gSmiTickPeriodTable;
    417 }
    418 
    419 /**
    420   This function returns the time in 100ns units since the periodic SMI
    421   handler function was called.  If the periodic SMI handler was resumed
    422   through PeriodicSmiYield(), then the time returned is the time in
    423   100ns units since PeriodicSmiYield() returned.
    424 
    425   @return  The actual time in 100ns units that the periodic SMI handler
    426            has been executing.  If this function is not called from within
    427            an enabled periodic SMI handler, then 0 is returned.
    428 
    429 **/
    430 UINT64
    431 EFIAPI
    432 PeriodicSmiExecutionTime (
    433   VOID
    434   )
    435 {
    436   PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT  *PeriodicSmiLibraryHandler;
    437   UINT64                                Current;
    438   UINT64                                Count;
    439 
    440   //
    441   // If there is no active periodic SMI handler, then return 0
    442   //
    443   PeriodicSmiLibraryHandler = GetActivePeriodicSmiLibraryHandler ();
    444   if (PeriodicSmiLibraryHandler == NULL) {
    445     return 0;
    446   }
    447 
    448   //
    449   // Get the current performance counter value
    450   //
    451   Current = GetPerformanceCounter ();
    452 
    453   //
    454   // Count the number of performance counter ticks since the periodic SMI handler
    455   // was dispatched or the last time this function was called.
    456   //
    457   if (PeriodicSmiLibraryHandler->PerfomanceCounterEndValue > PeriodicSmiLibraryHandler->PerfomanceCounterStartValue) {
    458     //
    459     // The performance counter counts up.  Check for roll over condition.
    460     //
    461     if (Current > PeriodicSmiLibraryHandler->DispatchCheckPointTime) {
    462       Count = Current - PeriodicSmiLibraryHandler->DispatchCheckPointTime;
    463     } else {
    464       Count = (Current - PeriodicSmiLibraryHandler->PerfomanceCounterStartValue) + (PeriodicSmiLibraryHandler->PerfomanceCounterEndValue - PeriodicSmiLibraryHandler->DispatchCheckPointTime);
    465     }
    466   } else {
    467     //
    468     // The performance counter counts down.  Check for roll over condition.
    469     //
    470     if (PeriodicSmiLibraryHandler->DispatchCheckPointTime > Current) {
    471       Count = PeriodicSmiLibraryHandler->DispatchCheckPointTime - Current;
    472     } else {
    473       Count = (PeriodicSmiLibraryHandler->DispatchCheckPointTime - PeriodicSmiLibraryHandler->PerfomanceCounterEndValue) + (PeriodicSmiLibraryHandler->PerfomanceCounterStartValue - Current);
    474     }
    475   }
    476 
    477   //
    478   // Accumulate the total number of performance counter ticks since the periodic
    479   // SMI handler was dispatched or resumed.
    480   //
    481   PeriodicSmiLibraryHandler->DispatchTotalTime += Count;
    482 
    483   //
    484   // Update the checkpoint value to the current performance counter value
    485   //
    486   PeriodicSmiLibraryHandler->DispatchCheckPointTime = Current;
    487 
    488   //
    489   // Convert the total number of performance counter ticks to 100 ns units
    490   //
    491   return DivU64x64Remainder (
    492            MultU64x32 (PeriodicSmiLibraryHandler->DispatchTotalTime, 10000000),
    493            PeriodicSmiLibraryHandler->PerfomanceCounterRate,
    494            NULL
    495            );
    496 }
    497 
    498 /**
    499   This function returns control back to the SMM Foundation.  When the next
    500   periodic SMI for the currently executing handler is triggered, the periodic
    501   SMI handler will restarted from its registered DispatchFunction entry point.
    502   If this function is not called from within an enabled periodic SMI handler,
    503   then control is returned to the calling function.
    504 
    505 **/
    506 VOID
    507 EFIAPI
    508 PeriodicSmiExit (
    509   VOID
    510   )
    511 {
    512   PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT  *PeriodicSmiLibraryHandler;
    513 
    514   //
    515   // If there is no active periodic SMI handler, then return
    516   //
    517   PeriodicSmiLibraryHandler = GetActivePeriodicSmiLibraryHandler ();
    518   if (PeriodicSmiLibraryHandler == NULL) {
    519     return;
    520   }
    521 
    522   //
    523   // Perform a long jump back to the point when the currently executing dispatch
    524   // function was dispatched.
    525   //
    526   LongJump (&PeriodicSmiLibraryHandler->DispatchJumpBuffer, 1);
    527 
    528   //
    529   // Must never return
    530   //
    531   ASSERT (FALSE);
    532   CpuDeadLoop();
    533 }
    534 
    535 /**
    536   This function yields control back to the SMM Foundation.  When the next
    537   periodic SMI for the currently executing handler is triggered, the periodic
    538   SMI handler will be resumed and this function will return.  Use of this
    539   function requires a seperate stack for the periodic SMI handler.  A non zero
    540   stack size must be specified in PeriodicSmiEnable() for this function to be
    541   used.
    542 
    543   If the stack size passed into PeriodicSmiEnable() was zero, the 0 is returned.
    544 
    545   If this function is not called from within an enabled periodic SMI handler,
    546   then 0 is returned.
    547 
    548   @return  The actual time in 100ns units elasped since this function was
    549            called.  A value of 0 indicates an unknown amount of time.
    550 
    551 **/
    552 UINT64
    553 EFIAPI
    554 PeriodicSmiYield (
    555   VOID
    556   )
    557 {
    558   PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT  *PeriodicSmiLibraryHandler;
    559   UINTN                                 SetJumpFlag;
    560 
    561   //
    562   // If there is no active periodic SMI handler, then return
    563   //
    564   PeriodicSmiLibraryHandler = GetActivePeriodicSmiLibraryHandler ();
    565   if (PeriodicSmiLibraryHandler == NULL) {
    566     return 0;
    567   }
    568 
    569   //
    570   // If PeriodicSmiYield() is called without an allocated stack, then just return
    571   // immediately with an elapsed time of 0.
    572   //
    573   if (PeriodicSmiLibraryHandler->Stack == NULL) {
    574     return 0;
    575   }
    576 
    577   //
    578   // Set a flag so the next periodic SMI event will resume at where SetJump()
    579   // is called below.
    580   //
    581   PeriodicSmiLibraryHandler->YieldFlag = TRUE;
    582 
    583   //
    584   // Save context in YieldJumpBuffer
    585   //
    586   SetJumpFlag = SetJump (&PeriodicSmiLibraryHandler->YieldJumpBuffer);
    587   if (SetJumpFlag == 0) {
    588     //
    589     // The intial call to SetJump() always returns 0.
    590     // If this is the initial call, then exit the current periodic SMI handler
    591     //
    592     PeriodicSmiExit ();
    593   }
    594 
    595   //
    596   // We get here when a LongJump is performed from PeriodicSmiDispatchFunctionOnCpu()
    597   // to resume a periodic SMI handler that called PeriodicSmiYield() on the
    598   // previous time this periodic SMI handler was dispatched.
    599   //
    600   // Clear the flag so the next periodic SMI dispatch will not resume.
    601   //
    602   PeriodicSmiLibraryHandler->YieldFlag = FALSE;
    603 
    604   //
    605   // Return the amount elapsed time that occured while yielded
    606   //
    607   return PeriodicSmiLibraryHandler->ElapsedTime;
    608 }
    609 
    610 /**
    611   Internal worker function that transfers control to an enabled periodic SMI
    612   handler.  If the enabled periodic SMI handler was allocated its own stack,
    613   then this function is called on that allocated stack through the BaseLin
    614   function SwitchStack().
    615 
    616   @param[in] Context1  Context1 parameter passed into SwitchStack().
    617   @param[in] Context2  Context2 parameter passed into SwitchStack().
    618 
    619 **/
    620 VOID
    621 EFIAPI
    622 PeriodicSmiDispatchFunctionSwitchStack (
    623   IN VOID  *Context1,  OPTIONAL
    624   IN VOID  *Context2   OPTIONAL
    625   )
    626 {
    627   PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT  *PeriodicSmiLibraryHandler;
    628 
    629   //
    630   // Convert Context1 to PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *
    631   //
    632   PeriodicSmiLibraryHandler = (PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *)Context1;
    633 
    634   //
    635   // Dispatch the registered handler passing in the context that was registered
    636   // and the amount of time that has elapsed since the previous time this
    637   // periodic SMI handler was dispacthed.
    638   //
    639   PeriodicSmiLibraryHandler->DispatchFunction (
    640     PeriodicSmiLibraryHandler->Context,
    641     PeriodicSmiLibraryHandler->ElapsedTime
    642     );
    643 
    644   //
    645   // If this DispatchFunction() returns, then unconditially call PeriodicSmiExit()
    646   // to perform a LongJump() back to PeriodicSmiDispatchFunctionOnCpu(). The
    647   // LongJump() will resume exection on the original stack.
    648   //
    649   PeriodicSmiExit ();
    650 }
    651 
    652 /**
    653   Internal worker function that transfers control to an enabled periodic SMI
    654   handler on the specified logial CPU.  This function determines if the periodic
    655   SMI handler yielded and needs to be resumed.  It also and switches to an
    656   allocated stack if one was allocated in PeriodicSmiEnable().
    657 
    658   @param[in] PeriodicSmiLibraryHandler  A pointer to the context for the periodic
    659                                         SMI handler to execute.
    660 
    661 **/
    662 VOID
    663 EFIAPI
    664 PeriodicSmiDispatchFunctionOnCpu (
    665   PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT  *PeriodicSmiLibraryHandler
    666   )
    667 {
    668   //
    669   // Save context in DispatchJumpBuffer.  The intial call to SetJump() always
    670   // returns 0.  If this is the initial call, then either resume from a prior
    671   // call to PeriodicSmiYield() or call the DispatchFunction registerd in
    672   // PeriodicSmiEnable() using an allocated stack if one was specified.
    673   //
    674   if (SetJump (&PeriodicSmiLibraryHandler->DispatchJumpBuffer) != 0) {
    675     return;
    676   }
    677 
    678   //
    679   // Capture the performance counter value just before the periodic SMI handler
    680   // is resumed so the amount of time the periodic SMI handler executes can be
    681   // calculated.
    682   //
    683   PeriodicSmiLibraryHandler->DispatchTotalTime      = 0;
    684   PeriodicSmiLibraryHandler->DispatchCheckPointTime = GetPerformanceCounter();
    685 
    686   if (PeriodicSmiLibraryHandler->YieldFlag) {
    687     //
    688     // Perform a long jump back to the point where the previously dispatched
    689     // function called PeriodicSmiYield().
    690     //
    691     LongJump (&PeriodicSmiLibraryHandler->YieldJumpBuffer, 1);
    692   } else if (PeriodicSmiLibraryHandler->Stack == NULL) {
    693     //
    694     // If Stack is NULL then call DispatchFunction using current stack passing
    695     // in the context that was registered and the amount of time that has
    696     // elapsed since the previous time this periodic SMI handler was dispacthed.
    697     //
    698     PeriodicSmiLibraryHandler->DispatchFunction (
    699       PeriodicSmiLibraryHandler->Context,
    700       PeriodicSmiLibraryHandler->ElapsedTime
    701       );
    702 
    703     //
    704     // If this DispatchFunction() returns, then unconditially call PeriodicSmiExit()
    705     // to perform a LongJump() back to this function.
    706     //
    707     PeriodicSmiExit ();
    708   } else {
    709     //
    710     // If Stack is not NULL then call DispatchFunction switching to the allocated stack
    711     //
    712     SwitchStack (
    713       PeriodicSmiDispatchFunctionSwitchStack,
    714       PeriodicSmiLibraryHandler,
    715       NULL,
    716       (UINT8 *)PeriodicSmiLibraryHandler->Stack + PeriodicSmiLibraryHandler->StackSize
    717       );
    718   }
    719 
    720   //
    721   // Must never return
    722   //
    723   ASSERT (FALSE);
    724   CpuDeadLoop();
    725 }
    726 
    727 /**
    728   Internal worker function that transfers control to an enabled periodic SMI
    729   handler on the specified logial CPU.  This worker function is only called
    730   using the SMM Services Table function SmmStartupThisAp() to execute the
    731   periodic SMI handler on a logical CPU that is different than the one that is
    732   running the SMM Foundation.  When the periodic SMI handler returns, a lock is
    733   released to notify the CPU that is running the SMM Foundation that the periodic
    734   SMI handler execution has finished its execution.
    735 
    736   @param[in, out] Buffer  A pointer to the context for the periodic SMI handler.
    737 
    738 **/
    739 VOID
    740 EFIAPI
    741 PeriodicSmiDispatchFunctionWithLock (
    742   IN OUT VOID  *Buffer
    743   )
    744 {
    745   PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT  *PeriodicSmiLibraryHandler;
    746 
    747   //
    748   // Get context
    749   //
    750   PeriodicSmiLibraryHandler = (PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT  *)Buffer;
    751 
    752   //
    753   // Execute dispatch function on the currently excuting logical CPU
    754   //
    755   PeriodicSmiDispatchFunctionOnCpu (PeriodicSmiLibraryHandler);
    756 
    757   //
    758   // Release the dispatch spin lock
    759   //
    760   ReleaseSpinLock (&PeriodicSmiLibraryHandler->DispatchLock);
    761 }
    762 
    763 /**
    764   Internal worker function that transfers control to a periodic SMI handler that
    765   was enabled using PeriodicSmiEnable().
    766 
    767   @param[in]     DispatchHandle  The unique handle assigned to this handler by
    768                                  SmiHandlerRegister().
    769   @param[in]     Context         Points to an optional handler context which was
    770                                  specified when the handler was registered.
    771   @param[in, out] CommBuffer     A pointer to a collection of data in memory that
    772                                  will be conveyed from a non-SMM environment into
    773                                  an SMM environment.
    774   @param[in, out] CommBufferSize The size of the CommBuffer.
    775 
    776   @retval EFI_SUCCESS                         The interrupt was handled and quiesced.
    777                                               No other handlers should still be called.
    778   @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED  The interrupt has been quiesced but other
    779                                               handlers should still be called.
    780   @retval EFI_WARN_INTERRUPT_SOURCE_PENDING   The interrupt is still pending and other
    781                                               handlers should still be called.
    782   @retval EFI_INTERRUPT_PENDING               The interrupt could not be quiesced.
    783 
    784 **/
    785 EFI_STATUS
    786 EFIAPI
    787 PeriodicSmiDispatchFunction (
    788   IN EFI_HANDLE  DispatchHandle,
    789   IN CONST VOID  *Context         OPTIONAL,
    790   IN OUT VOID    *CommBuffer      OPTIONAL,
    791   IN OUT UINTN   *CommBufferSize  OPTIONAL
    792   )
    793 {
    794   PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT  *PeriodicSmiLibraryHandler;
    795   EFI_SMM_PERIODIC_TIMER_CONTEXT        *TimerContext;
    796   EFI_STATUS                            Status;
    797 
    798   //
    799   // Set the active periodic SMI handler
    800   //
    801   PeriodicSmiLibraryHandler = SetActivePeriodicSmiLibraryHandler (Context);
    802   if (PeriodicSmiLibraryHandler == NULL) {
    803     return EFI_NOT_FOUND;
    804   }
    805 
    806   //
    807   // Retrieve the elapsed time since the last time this periodic SMI handler was called
    808   //
    809   PeriodicSmiLibraryHandler->ElapsedTime = 0;
    810   if (CommBuffer != NULL) {
    811     TimerContext = (EFI_SMM_PERIODIC_TIMER_CONTEXT  *)CommBuffer;
    812     PeriodicSmiLibraryHandler->ElapsedTime = TimerContext->ElapsedTime;
    813   }
    814 
    815   //
    816   // Dispatch the periodic SMI handler
    817   //
    818   if ((PeriodicSmiLibraryHandler->Cpu == PERIODIC_SMI_LIBRARY_ANY_CPU) ||
    819       (PeriodicSmiLibraryHandler->Cpu == gSmst->CurrentlyExecutingCpu)    ) {
    820     //
    821     // Dispatch on the currently execution CPU if the CPU specified in PeriodicSmiEnable()
    822     // was PERIODIC_SMI_LIBARRY_ANY_CPU or the currently executing CPU matches the CPU
    823     // that was specified in PeriodicSmiEnable().
    824     //
    825     PeriodicSmiDispatchFunctionOnCpu (PeriodicSmiLibraryHandler);
    826   } else {
    827     //
    828     // Acquire spin lock for ths periodic SMI handler.  The AP will release the
    829     // spin lock when it is done executing the periodic SMI handler.
    830     //
    831     AcquireSpinLock (&PeriodicSmiLibraryHandler->DispatchLock);
    832 
    833     //
    834     // Execute the periodic SMI handler on the CPU that was specified in
    835     // PeriodicSmiEnable().
    836     //
    837     Status = gSmst->SmmStartupThisAp (
    838                       PeriodicSmiDispatchFunctionWithLock,
    839                       PeriodicSmiLibraryHandler->Cpu,
    840                       PeriodicSmiLibraryHandler
    841                       );
    842     if (!EFI_ERROR (Status)) {
    843       //
    844       // Wait for the AP to release the spin lock.
    845       //
    846       while (!AcquireSpinLockOrFail (&PeriodicSmiLibraryHandler->DispatchLock)) {
    847         CpuPause ();
    848       }
    849     }
    850 
    851     //
    852     // Release the spin lock for the periodic SMI handler.
    853     //
    854     ReleaseSpinLock (&PeriodicSmiLibraryHandler->DispatchLock);
    855   }
    856 
    857   //
    858   // Reclaim the active periodic SMI handler if it was disabled during the current dispatch.
    859   //
    860   if (PeriodicSmiLibraryHandler->DispatchHandle == NULL) {
    861     ReclaimPeriodicSmiLibraryHandler (PeriodicSmiLibraryHandler);
    862   }
    863 
    864   //
    865   // Update state to show that there is no active periodic SMI handler
    866   //
    867   SetActivePeriodicSmiLibraryHandler (NULL);
    868 
    869   return EFI_SUCCESS;
    870 }
    871 
    872 /**
    873   This function enables a periodic SMI handler.
    874 
    875   @param[in, out] DispatchHandle   A pointer to the handle associated with the
    876                                    enabled periodic SMI handler.  This is an
    877                                    optional parameter that may be NULL.  If it is
    878                                    NULL, then the handle will not be returned,
    879                                    which means that the periodic SMI handler can
    880                                    never be disabled.
    881   @param[in]     DispatchFunction  A pointer to a periodic SMI handler function.
    882   @param[in]     Context           Optional content to pass into DispatchFunction.
    883   @param[in]     TickPeriod        The requested tick period in 100ns units that
    884                                    control should be givien to the periodic SMI
    885                                    handler.  Must be one of the supported values
    886                                    returned by PeriodicSmiSupportedPickPeriod().
    887   @param[in]     Cpu               Specifies the CPU that is required to execute
    888                                    the periodic SMI handler.  If Cpu is
    889                                    PERIODIC_SMI_LIBRARY_ANY_CPU, then the periodic
    890                                    SMI handler will always be executed on the SMST
    891                                    CurrentlyExecutingCpu, which may vary across
    892                                    periodic SMIs.  If Cpu is between 0 and the SMST
    893                                    NumberOfCpus, then the periodic SMI will always
    894                                    be executed on the requested CPU.
    895   @param[in]     StackSize         The size, in bytes, of the stack to allocate for
    896                                    use by the periodic SMI handler.  If 0, then the
    897                                    default stack will be used.
    898 
    899   @retval EFI_INVALID_PARAMETER  DispatchFunction is NULL.
    900   @retval EFI_UNSUPPORTED        TickPeriod is not a supported tick period.  The
    901                                  supported tick periods can be retrieved using
    902                                  PeriodicSmiSupportedTickPeriod().
    903   @retval EFI_INVALID_PARAMETER  Cpu is not PERIODIC_SMI_LIBRARY_ANY_CPU or in
    904                                  the range 0 to SMST NumberOfCpus.
    905   @retval EFI_OUT_OF_RESOURCES   There are not enough resources to enable the
    906                                  periodic SMI handler.
    907   @retval EFI_OUT_OF_RESOURCES   There are not enough resources to allocate the
    908                                  stack speficied by StackSize.
    909   @retval EFI_SUCCESS            The periodic SMI handler was enabled.
    910 
    911 **/
    912 EFI_STATUS
    913 EFIAPI
    914 PeriodicSmiEnable (
    915   IN OUT EFI_HANDLE                    *DispatchHandle,    OPTIONAL
    916   IN     PERIODIC_SMI_LIBRARY_HANDLER  DispatchFunction,
    917   IN     CONST VOID                    *Context,           OPTIONAL
    918   IN     UINT64                        TickPeriod,
    919   IN     UINTN                         Cpu,
    920   IN     UINTN                         StackSize
    921   )
    922 {
    923   EFI_STATUS                            Status;
    924   UINTN                                 Index;
    925   PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT  *PeriodicSmiLibraryHandler;
    926 
    927   //
    928   // Make sure all the input parameters are valid
    929   //
    930   if (DispatchFunction == NULL) {
    931     return EFI_INVALID_PARAMETER;
    932   }
    933 
    934   for (Index = 0; gSmiTickPeriodTable[Index] != 0; Index++) {
    935     if (gSmiTickPeriodTable[Index] == TickPeriod) {
    936       break;
    937     }
    938   }
    939   if (gSmiTickPeriodTable[Index] == 0) {
    940     return EFI_UNSUPPORTED;
    941   }
    942 
    943   if (Cpu != PERIODIC_SMI_LIBRARY_ANY_CPU && Cpu >= gSmst->NumberOfCpus) {
    944     return EFI_INVALID_PARAMETER;
    945   }
    946 
    947   //
    948   // Find a free periodic SMI handler entry
    949   //
    950   PeriodicSmiLibraryHandler = FindFreePeriodicSmiLibraryHandler();
    951   if (PeriodicSmiLibraryHandler == NULL) {
    952     return EFI_OUT_OF_RESOURCES;
    953   }
    954 
    955   //
    956   // Initialize a new periodic SMI handler entry
    957   //
    958   PeriodicSmiLibraryHandler->YieldFlag        = FALSE;
    959   PeriodicSmiLibraryHandler->DispatchHandle   = NULL;
    960   PeriodicSmiLibraryHandler->DispatchFunction = DispatchFunction;
    961   PeriodicSmiLibraryHandler->Context          = (VOID *)Context;
    962   PeriodicSmiLibraryHandler->Cpu              = Cpu;
    963   PeriodicSmiLibraryHandler->StackSize        = ALIGN_VALUE (StackSize, EFI_PAGE_SIZE);
    964   if (PeriodicSmiLibraryHandler->StackSize > 0) {
    965     PeriodicSmiLibraryHandler->Stack = AllocatePages (EFI_SIZE_TO_PAGES (PeriodicSmiLibraryHandler->StackSize));
    966     if (PeriodicSmiLibraryHandler->Stack == NULL) {
    967       return EFI_OUT_OF_RESOURCES;
    968     }
    969     ZeroMem (PeriodicSmiLibraryHandler->Stack, PeriodicSmiLibraryHandler->StackSize);
    970   } else {
    971     PeriodicSmiLibraryHandler->Stack = NULL;
    972   }
    973   InitializeSpinLock (&PeriodicSmiLibraryHandler->DispatchLock);
    974   PeriodicSmiLibraryHandler->PerfomanceCounterRate = GetPerformanceCounterProperties (
    975                                                        &PeriodicSmiLibraryHandler->PerfomanceCounterStartValue,
    976                                                        &PeriodicSmiLibraryHandler->PerfomanceCounterEndValue
    977                                                        );
    978   PeriodicSmiLibraryHandler->RegisterContext.Period          = TickPeriod;
    979   PeriodicSmiLibraryHandler->RegisterContext.SmiTickInterval = TickPeriod;
    980   Status = gSmmPeriodicTimerDispatch2->Register (
    981                                          gSmmPeriodicTimerDispatch2,
    982                                          PeriodicSmiDispatchFunction,
    983                                          &PeriodicSmiLibraryHandler->RegisterContext,
    984                                          &PeriodicSmiLibraryHandler->DispatchHandle
    985                                          );
    986   if (EFI_ERROR (Status)) {
    987     PeriodicSmiLibraryHandler->DispatchHandle = NULL;
    988     ReclaimPeriodicSmiLibraryHandler (PeriodicSmiLibraryHandler);
    989     return EFI_OUT_OF_RESOURCES;
    990   }
    991 
    992   //
    993   // Return the registered handle if the optional DispatchHandle parameter is not NULL
    994   //
    995   if (DispatchHandle != NULL) {
    996     *DispatchHandle = PeriodicSmiLibraryHandler->DispatchHandle;
    997   }
    998   return EFI_SUCCESS;
    999 }
   1000 
   1001 /**
   1002   This function disables a periodic SMI handler that has been previously
   1003   enabled with PeriodicSmiEnable().
   1004 
   1005   @param[in] DispatchHandle  A handle associated with a previously enabled periodic
   1006                              SMI handler.  This is an optional parameter that may
   1007                              be NULL.  If it is NULL, then the active periodic SMI
   1008                              handlers is disabled.
   1009 
   1010   @retval FALSE  DispatchHandle is NULL and there is no active periodic SMI handler.
   1011   @retval FALSE  The periodic SMI handler specified by DispatchHandle has
   1012                  not been enabled with PeriodicSmiEnable().
   1013   @retval TRUE   The periodic SMI handler specified by DispatchHandle has
   1014                  been disabled.  If DispatchHandle is NULL, then the active
   1015                  periodic SMI handler has been disabled.
   1016 
   1017 **/
   1018 BOOLEAN
   1019 EFIAPI
   1020 PeriodicSmiDisable (
   1021   IN EFI_HANDLE  DispatchHandle    OPTIONAL
   1022   )
   1023 {
   1024   PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT  *PeriodicSmiLibraryHandler;
   1025   EFI_STATUS                            Status;
   1026 
   1027   //
   1028   // Lookup the periodic SMI handler specified by DispatchHandle
   1029   //
   1030   PeriodicSmiLibraryHandler = LookupPeriodicSmiLibraryHandler (DispatchHandle);
   1031   if (PeriodicSmiLibraryHandler == NULL) {
   1032     return FALSE;
   1033   }
   1034 
   1035   //
   1036   // Unregister the periodic SMI handler from the SMM Periodic Timer Dispatch 2 Protocol
   1037   //
   1038   Status = gSmmPeriodicTimerDispatch2->UnRegister (
   1039                                          gSmmPeriodicTimerDispatch2,
   1040                                          PeriodicSmiLibraryHandler->DispatchHandle
   1041                                          );
   1042   if (EFI_ERROR (Status)) {
   1043     return FALSE;
   1044   }
   1045 
   1046   //
   1047   // Mark the entry for the disabled periodic SMI handler as free, and
   1048   // call ReclaimPeriodicSmiLibraryHandler to move it to the list of free
   1049   // periodic SMI handlers.
   1050   //
   1051   PeriodicSmiLibraryHandler->DispatchHandle = NULL;
   1052   if (PeriodicSmiLibraryHandler != GetActivePeriodicSmiLibraryHandler ()) {
   1053     ReclaimPeriodicSmiLibraryHandler (PeriodicSmiLibraryHandler);
   1054   }
   1055 
   1056   return TRUE;
   1057 }
   1058 
   1059 /**
   1060   This constructor function caches the pointer to the SMM Periodic Timer
   1061   Dispatch 2 Protocol and collects the list SMI tick rates that the hardware
   1062   supports.
   1063 
   1064   @param[in] ImageHandle  The firmware allocated handle for the EFI image.
   1065   @param[in] SystemTable  A pointer to the EFI System Table.
   1066 
   1067   @retval EFI_SUCCESS   The constructor always returns EFI_SUCCESS.
   1068 
   1069 **/
   1070 EFI_STATUS
   1071 EFIAPI
   1072 SmmPeriodicSmiLibConstructor (
   1073   IN EFI_HANDLE        ImageHandle,
   1074   IN EFI_SYSTEM_TABLE  *SystemTable
   1075   )
   1076 {
   1077   EFI_STATUS  Status;
   1078   UINT64      *SmiTickInterval;
   1079   UINTN       Count;
   1080 
   1081   //
   1082   // Locate the SMM Periodic Timer Dispatch 2 Protocol
   1083   //
   1084   Status = gSmst->SmmLocateProtocol (
   1085                     &gEfiSmmPeriodicTimerDispatch2ProtocolGuid,
   1086                     NULL,
   1087                     (VOID **)&gSmmPeriodicTimerDispatch2
   1088                     );
   1089   ASSERT_EFI_ERROR (Status);
   1090   ASSERT (gSmmPeriodicTimerDispatch2 != NULL);
   1091 
   1092   //
   1093   // Count the number of periodic SMI tick intervals that the SMM Periodic Timer
   1094   // Dipatch 2 Protocol supports.
   1095   //
   1096   SmiTickInterval = NULL;
   1097   Count = 0;
   1098   do {
   1099     Status = gSmmPeriodicTimerDispatch2->GetNextShorterInterval (
   1100                                            gSmmPeriodicTimerDispatch2,
   1101                                            &SmiTickInterval
   1102                                            );
   1103     Count++;
   1104   } while (SmiTickInterval != NULL);
   1105 
   1106   //
   1107   // Allocate a buffer for the table of supported periodic SMI tick periods.
   1108   //
   1109   gSmiTickPeriodTable = AllocateZeroPool (Count * sizeof (UINT64));
   1110   ASSERT (gSmiTickPeriodTable != NULL);
   1111 
   1112   //
   1113   // Fill in the table of supported periodic SMI tick periods.
   1114   //
   1115   SmiTickInterval = NULL;
   1116   Count = 0;
   1117   do {
   1118     gSmiTickPeriodTable[Count] = 0;
   1119     Status = gSmmPeriodicTimerDispatch2->GetNextShorterInterval (
   1120                                            gSmmPeriodicTimerDispatch2,
   1121                                            &SmiTickInterval
   1122                                            );
   1123     if (SmiTickInterval != NULL) {
   1124       gSmiTickPeriodTable[Count] = *SmiTickInterval;
   1125     }
   1126     Count++;
   1127   } while (SmiTickInterval != NULL);
   1128 
   1129   //
   1130   // Allocate buffer for initial set of periodic SMI handlers
   1131   //
   1132   EnlargeFreePeriodicSmiLibraryHandlerList ();
   1133 
   1134   return EFI_SUCCESS;
   1135 }
   1136 
   1137 /**
   1138   The constructor function caches the pointer to the SMM Periodic Timer Dispatch 2
   1139   Protocol and collects the list SMI tick rates that the hardware supports.
   1140 
   1141   @param[in] ImageHandle  The firmware allocated handle for the EFI image.
   1142   @param[in] SystemTable  A pointer to the EFI System Table.
   1143 
   1144   @retval EFI_SUCCESS   The constructor always returns EFI_SUCCESS.
   1145 
   1146 **/
   1147 EFI_STATUS
   1148 EFIAPI
   1149 SmmPeriodicSmiLibDestructor (
   1150   IN EFI_HANDLE        ImageHandle,
   1151   IN EFI_SYSTEM_TABLE  *SystemTable
   1152   )
   1153 {
   1154   LIST_ENTRY                            *Link;
   1155   PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT  *PeriodicSmiLibraryHandler;
   1156 
   1157   //
   1158   // Free the table of supported periodic SMI tick rates
   1159   //
   1160   if (gSmiTickPeriodTable != NULL) {
   1161     FreePool (gSmiTickPeriodTable);
   1162   }
   1163 
   1164   //
   1165   // Disable all periodic SMI handlers
   1166   //
   1167   for (Link = GetFirstNode (&gPeriodicSmiLibraryHandlers); !IsNull (&gPeriodicSmiLibraryHandlers, Link);) {
   1168     PeriodicSmiLibraryHandler = PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT_FROM_LINK (Link);
   1169     Link = GetNextNode (&gPeriodicSmiLibraryHandlers, Link);
   1170     PeriodicSmiDisable (PeriodicSmiLibraryHandler->DispatchHandle);
   1171   }
   1172 
   1173   //
   1174   // Free all the periodic SMI handler entries
   1175   //
   1176   for (Link = GetFirstNode (&gFreePeriodicSmiLibraryHandlers); !IsNull (&gFreePeriodicSmiLibraryHandlers, Link);) {
   1177     PeriodicSmiLibraryHandler = PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT_FROM_LINK (Link);
   1178     Link = RemoveEntryList (Link);
   1179     FreePool (PeriodicSmiLibraryHandler);
   1180   }
   1181 
   1182   return EFI_SUCCESS;
   1183 }
   1184