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 *
GetActivePeriodicSmiLibraryHandler(VOID)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 *
LookupPeriodicSmiLibraryHandler(IN EFI_HANDLE DispatchHandle OPTIONAL)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 *
SetActivePeriodicSmiLibraryHandler(IN CONST VOID * Context OPTIONAL)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
ReclaimPeriodicSmiLibraryHandler(PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT * PeriodicSmiLibraryHandler)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
EnlargeFreePeriodicSmiLibraryHandlerList(VOID)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 *
FindFreePeriodicSmiLibraryHandler(VOID)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
PeriodicSmiSupportedTickPeriod(VOID)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
PeriodicSmiExecutionTime(VOID)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
PeriodicSmiExit(VOID)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
PeriodicSmiYield(VOID)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
PeriodicSmiDispatchFunctionSwitchStack(IN VOID * Context1,OPTIONAL IN VOID * Context2 OPTIONAL)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
PeriodicSmiDispatchFunctionOnCpu(PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT * PeriodicSmiLibraryHandler)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
PeriodicSmiDispatchFunctionWithLock(IN OUT VOID * Buffer)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
PeriodicSmiDispatchFunction(IN EFI_HANDLE DispatchHandle,IN CONST VOID * Context OPTIONAL,IN OUT VOID * CommBuffer OPTIONAL,IN OUT UINTN * CommBufferSize OPTIONAL)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
PeriodicSmiEnable(IN OUT EFI_HANDLE * DispatchHandle,OPTIONAL IN PERIODIC_SMI_LIBRARY_HANDLER DispatchFunction,IN CONST VOID * Context,OPTIONAL IN UINT64 TickPeriod,IN UINTN Cpu,IN UINTN StackSize)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
PeriodicSmiDisable(IN EFI_HANDLE DispatchHandle OPTIONAL)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
SmmPeriodicSmiLibConstructor(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)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
SmmPeriodicSmiLibDestructor(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)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