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