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