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