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