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