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