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