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