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