summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormdkinney <mdkinney@6f19259b-4bc3-4df7-8a09-765794883524>2011-02-11 00:09:16 +0000
committermdkinney <mdkinney@6f19259b-4bc3-4df7-8a09-765794883524>2011-02-11 00:09:16 +0000
commit40039e28edd6f88614c0c0261bf78e74fde73bf0 (patch)
tree8df69a946c623c38fcaae8518fb6352dc9c50e0a
parent784ce127276c4a0cb42cd6b24bcec9072d4d3ce9 (diff)
Add SmmPeriodicSmiLib to MdePkg.
git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@11302 6f19259b-4bc3-4df7-8a09-765794883524
-rw-r--r--MdePkg/Include/Library/SmmPeriodicSmiLib.h184
-rw-r--r--MdePkg/Library/SmmPeriodicSmiLib/SmmPeriodicSmiLib.c1165
-rw-r--r--MdePkg/Library/SmmPeriodicSmiLib/SmmPeriodicSmiLib.inf50
-rw-r--r--MdePkg/MdePkg.dec6
-rw-r--r--MdePkg/MdePkg.dsc4
5 files changed, 1406 insertions, 3 deletions
diff --git a/MdePkg/Include/Library/SmmPeriodicSmiLib.h b/MdePkg/Include/Library/SmmPeriodicSmiLib.h
new file mode 100644
index 000000000..efdff6f44
--- /dev/null
+++ b/MdePkg/Include/Library/SmmPeriodicSmiLib.h
@@ -0,0 +1,184 @@
+/** @file
+ Provides services to enable and disable periodic SMI handlers.
+
+Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __PERIODIC_SMI_LIB_H__
+#define __PERIODIC_SMI_LIB_H__
+
+#define PERIODIC_SMI_LIBRARY_ANY_CPU 0xffffffff
+
+/**
+ This function returns a pointer to a table of supported periodic
+ SMI tick periods in 100 ns units sorted from largest to smallest.
+ The table contains a array of UINT64 values terminated by a tick
+ period value of 0. The returned table must be treated as read-only
+ data and must not be freed.
+
+ @return A pointer to a table of UINT64 tick period values in
+ 100ns units sorted from largest to smallest terminated
+ by a tick period of 0.
+
+**/
+UINT64 *
+EFIAPI
+PeriodicSmiSupportedTickPeriod (
+ VOID
+ );
+
+/**
+ This function returns the time in 100ns units since the periodic SMI
+ handler function was called. If the periodic SMI handler was resumed
+ through PeriodicSmiYield(), then the time returned is the time in
+ 100ns units since PeriodicSmiYield() returned.
+
+ @return The actual time in 100ns units that the periodic SMI handler
+ has been executing. If this function is not called from within
+ an enabled periodic SMI handler, then 0 is returned.
+
+**/
+UINT64
+EFIAPI
+PeriodicSmiExecutionTime (
+ VOID
+ );
+
+/**
+ This function returns control back to the SMM Foundation. When the next
+ periodic SMI for the currently executing handler is triggered, the periodic
+ SMI handler will restarted from its registered DispatchFunction entry point.
+ If this function is not called from within an enabled periodic SMI handler,
+ then control is returned to the calling function.
+
+**/
+VOID
+EFIAPI
+PeriodicSmiExit (
+ VOID
+ );
+
+/**
+ This function yields control back to the SMM Foundation. When the next
+ periodic SMI for the currently executing handler is triggered, the periodic
+ SMI handler will be resumed and this function will return. Use of this
+ function requires a seperate stack for the periodic SMI handler. A non zero
+ stack size must be specified in PeriodicSmiEnable() for this function to be
+ used.
+
+ If the stack size passed into PeriodicSmiEnable() was zero, the 0 is returned.
+
+ If this function is not called from within an enabled periodic SMI handler,
+ then 0 is returned.
+
+ @return The actual time in 100ns units elasped since this function was
+ called. A value of 0 indicates an unknown amount of time.
+
+**/
+UINT64
+EFIAPI
+PeriodicSmiYield (
+ VOID
+ );
+
+/**
+ This function is a prototype for a periodic SMI handler function
+ that may be enabled with PeriodicSmiEnable() and disabled with
+ PeriodicSmiDisable().
+
+ @param[in] Context Content registered with PeriodicSmiEnable().
+ @param[in] ElapsedTime The actual time in 100ns units elasped since
+ this function was called. A value of 0 indicates
+ an unknown amount of time.
+
+**/
+typedef
+VOID
+(EFIAPI *PERIODIC_SMI_LIBRARY_HANDLER) (
+ IN CONST VOID *Context OPTIONAL,
+ IN UINT64 ElapsedTime
+ );
+
+/**
+ This function enables a periodic SMI handler.
+
+ @param[in,out] DispatchHandle A pointer to the handle associated with the
+ enabled periodic SMI handler. This is an
+ optional parameter that may be NULL. If it is
+ NULL, then the handle will not be returned,
+ which means that the periodic SMI handler can
+ never be disabled.
+ @param[in] DispatchFunction A pointer to a periodic SMI handler function.
+ @param[in] Context Optional content to pass into DispatchFunction.
+ @param[in] TickPeriod The requested tick period in 100ns units that
+ control should be givien to the periodic SMI
+ handler. Must be one of the supported values
+ returned by PeriodicSmiSupportedPickPeriod().
+ @param[in] Cpu Specifies the CPU that is required to execute
+ the periodic SMI handler. If Cpu is
+ PERIODIC_SMI_LIBRARY_ANY_CPU, then the periodic
+ SMI handler will always be executed on the SMST
+ CurrentlyExecutingCpu, which may vary across
+ periodic SMIs. If Cpu is between 0 and the SMST
+ NumberOfCpus, then the periodic SMI will always
+ be executed on the requested CPU.
+ @param[in] StackSize The size, in bytes, of the stack to allocate for
+ use by the periodic SMI handler. If 0, then the
+ default stack will be used.
+
+ @retval EFI_INVALID_PARAMETER DispatchFunction is NULL.
+ @retval EFI_UNSUPPORTED TickPeriod is not a supported tick period. The
+ supported tick periods can be retrieved using
+ PeriodicSmiSupportedTickPeriod().
+ @retval EFI_INVALID_PARAMETER Cpu is not PERIODIC_SMI_LIBRARY_ANY_CPU or in
+ the range 0 to SMST NumberOfCpus.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to enable the
+ periodic SMI handler.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate the
+ stack speficied by StackSize.
+ @retval EFI_SUCCESS The periodic SMI handler was enabled.
+
+**/
+EFI_STATUS
+EFIAPI
+PeriodicSmiEnable (
+ IN OUT EFI_HANDLE *DispatchHandle, OPTIONAL
+ IN PERIODIC_SMI_LIBRARY_HANDLER DispatchFunction,
+ IN CONST VOID *Context, OPTIONAL
+ IN UINT64 TickPeriod,
+ IN UINTN Cpu,
+ IN UINTN StackSize
+ );
+
+/**
+ This function disables a periodic SMI handler that has been previously
+ enabled with PeriodicSmiEnable().
+
+ @param[in] DispatchHandle A handle associated with a previously enabled periodic
+ SMI handler. This is an optional parameter that may
+ be NULL. If it is NULL, then the active periodic SMI
+ handlers is disabled.
+
+ @retval FALSE DispatchHandle is NULL and there is no active periodic SMI handler.
+ @retval FALSE The periodic SMI handler specified by DispatchHandle has
+ not been enabled with PeriodicSmiEnable().
+ @retval TRUE The periodic SMI handler specified by DispatchHandle has
+ been disabled. If DispatchHandle is NULL, then the active
+ periodic SMI handler has been disabled.
+
+**/
+BOOLEAN
+EFIAPI
+PeriodicSmiDisable (
+ IN EFI_HANDLE DispatchHandle OPTIONAL
+ );
+
+#endif
diff --git a/MdePkg/Library/SmmPeriodicSmiLib/SmmPeriodicSmiLib.c b/MdePkg/Library/SmmPeriodicSmiLib/SmmPeriodicSmiLib.c
new file mode 100644
index 000000000..a941d75e5
--- /dev/null
+++ b/MdePkg/Library/SmmPeriodicSmiLib/SmmPeriodicSmiLib.c
@@ -0,0 +1,1165 @@
+/** @file
+ SMM Periodic SMI Library.
+
+ Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php.
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include <PiSmm.h>
+
+#include <Protocol/SmmPeriodicTimerDispatch2.h>
+
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/SynchronizationLib.h>
+#include <Library/DebugLib.h>
+#include <Library/TimerLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/SmmServicesTableLib.h>
+
+#include <Library/SmmPeriodicSmiLib.h>
+
+///
+/// Define the number of periodic SMI handler entries that should be allocated in
+/// the constructor for gPeriodicSmiLibraryHandlers and also use this value as the
+/// number of entries to add to gPeriodicSmiLibraryHandlers when gPeriodicSmiLibraryHandlers
+/// is full.
+///
+#define PERIODIC_SMI_LIBRARY_ALLOCATE_SIZE 0x08
+
+///
+/// Signature for a PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT structure
+///
+#define PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT_SIGNATURE SIGNATURE_32 ('P', 'S', 'M', 'I')
+
+///
+/// Structure that contains state information for an enabled periodic SMI handler
+///
+typedef struct {
+ ///
+ /// Signature value that must be set to PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT_SIGNATURE
+ ///
+ UINT32 Signature;
+ ///
+ /// The dispatch function to called to invoke an enabled periodic SMI handler.
+ ///
+ PERIODIC_SMI_LIBRARY_HANDLER DispatchFunction;
+ ///
+ /// The context to pass into DispatchFunction
+ ///
+ VOID *Context;
+ ///
+ /// The tick period in 100 ns units that DispatchFunction should be called.
+ ///
+ UINT64 TickPeriod;
+ ///
+ /// The Cpu number that is required to execute DispatchFunction. If Cpu is
+ /// set to PERIODIC_SMI_LIBRARY_ANY_CPU, then DispatchFunction may be executed
+ /// on any CPU.
+ ///
+ UINTN Cpu;
+ ///
+ /// The size, in bytes, of the stack allocated for a periodic SMI handler.
+ /// This value must be a multiple of EFI_PAGE_SIZE.
+ ///
+ UINTN StackSize;
+ ///
+ /// A pointer to the stack allocated using AllocatePages(). This field will
+ /// be NULL if StackSize is 0.
+ ///
+ VOID *Stack;
+ ///
+ /// Spin lock used to wait for an AP to complete the execution of a periodic SMI handler
+ ///
+ SPIN_LOCK DispatchLock;
+ ///
+ /// The rate in Hz of the performance counter that is used to measure the
+ /// amount of time that a periodic SMI handler executes.
+ ///
+ UINT64 PerfomanceCounterRate;
+ ///
+ /// The start count value of the performance counter that is used to measure
+ /// the amount of time that a periodic SMI handler executes.
+ ///
+ UINT64 PerfomanceCounterStartValue;
+ ///
+ /// The end count value of the performance counter that is used to measure
+ /// the amount of time that a periodic SMI handler executes.
+ ///
+ UINT64 PerfomanceCounterEndValue;
+ ///
+ /// The context record passed into the Register() function of the SMM Periodic
+ /// Timer Dispatch Protocol when a periodic SMI handler is enabled.
+ ///
+ EFI_SMM_PERIODIC_TIMER_REGISTER_CONTEXT RegisterContext;
+ ///
+ /// The handle returned from the Register() function of the SMM Periodic
+ /// Timer Dispatch Protocol when a periodic SMI handler is enabled.
+ ///
+ EFI_HANDLE DispatchHandle;
+ ///
+ /// The total number of performance counter ticks that the periodic SMI handler
+ /// has been executing in its current invocation.
+ ///
+ UINT64 DispatchTotalTime;
+ ///
+ /// The performance counter value that was captured the last time that the
+ /// periodic SMI handler called PeriodcSmiExecutionTime(). This allows the
+ /// time value returned by PeriodcSmiExecutionTime() to be accurate even when
+ /// the performance counter rolls over.
+ ///
+ UINT64 DispatchCheckPointTime;
+ ///
+ /// Buffer used to save the context when control is transfer from this library
+ /// to an enabled periodic SMI handler. This saved context is used when the
+ /// periodic SMI handler exits or yields.
+ ///
+ BASE_LIBRARY_JUMP_BUFFER DispatchJumpBuffer;
+ ///
+ /// Flag that is set to TRUE when a periodic SMI handler requests to yield
+ /// using PeriodicSmiYield(). When this flag IS TRUE, YieldJumpBuffer is
+ /// valid. When this flag is FALSE, YieldJumpBuffer is not valid.
+ ///
+ BOOLEAN YieldFlag;
+ ///
+ /// Buffer used to save the context when a periodic SMI handler requests to
+ /// yield using PeriodicSmiYield(). This context is used to resume the
+ /// execution of a periodic SMI handler the next time control is transferd
+ /// to the periodic SMI handler that yielded.
+ ///
+ BASE_LIBRARY_JUMP_BUFFER YieldJumpBuffer;
+ ///
+ /// The amount of time, in 100 ns units, that have elapsed since the last
+ /// time the periodic SMI handler was invoked.
+ ///
+ UINT64 ElapsedTime;
+} PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT;
+
+/**
+ Macro that returns a pointer to a PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT
+ structure based on a pointer to a RegisterContext field.
+
+**/
+#define PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT_FROM_REGISTER_CONTEXT(a) \
+ CR ( \
+ a, \
+ PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT, \
+ RegisterContext, \
+ PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT_SIGNATURE \
+ )
+
+///
+/// Pointer to the SMM Periodic Timer Disatch Protocol that was located in the constuctor.
+///
+EFI_SMM_PERIODIC_TIMER_DISPATCH2_PROTOCOL *gSmmPeriodicTimerDispatch2 = NULL;
+
+///
+/// Pointer to a table of supported periodic SMI tick periods in 100 ns units
+/// sorted from largest to smallest terminated by a tick period value of 0.
+/// This table is allocated using AllocatePool() in the constructor and filled
+/// in based on the values returned from the SMM Periodic Timer Dispatch 2 Protocol
+/// function GetNextShorterInterval().
+///
+UINT64 *gSmiTickPeriodTable = NULL;
+
+///
+/// The number entries in gPeriodicSmiLibraryHandlers
+///
+UINTN gNumberOfPeriodicSmiLibraryHandlers = 0;
+
+///
+/// Table of periodic SMI handlers that this library is currently managing. This
+/// table is allocated using AllocatePool()
+///
+PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *gPeriodicSmiLibraryHandlers = NULL;
+
+///
+/// The index of gPeriodicSmiLibraryHandlers that is currently being executed.
+/// Is set to -1 if no periodic SMI handler is currently being executed.
+///
+INTN gActivePeriodicSmiLibraryHandlerIndex = -1;
+
+/**
+ Internal worker function that returns a pointer to the
+ PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT structure associated with the periodic
+ SMI handler that is currently being executed. If a periodic SMI handler is
+ not currently being executed, the NULL is returned.
+
+ @retval NULL A periodic SMI handler is not currently being executed.
+ @retval other Pointer to the PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT
+ associated with the active periodic SMI handler.
+
+**/
+PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *
+GetActivePeriodicSmiLibraryHandler (
+ VOID
+ )
+{
+ if (gActivePeriodicSmiLibraryHandlerIndex < 0) {
+ //
+ // Return NULL if index is negative, which means that there is no active
+ // periodic SMI handler.
+ //
+ return NULL;
+ }
+
+ //
+ // Return a pointer to the active periodic SMI handler context
+ //
+ return &gPeriodicSmiLibraryHandlers[gActivePeriodicSmiLibraryHandlerIndex];
+}
+
+/**
+ Internal worker function that returns a pointer to the
+ PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT structure associated with the
+ DispatchHandle that was returned when the periodic SMI handler was enabled
+ with PeriodicSmiEnable(). If DispatchHandle is NULL, then the active
+ periodic SMI handler is returned. If DispatchHandle is NULL and there is
+ no active periodic SMI handler, then NULL is returned.
+
+ @param[in] DispatchHandle DispatchHandle that was returned when the periodic
+ SMI handler was enabled with PeriodicSmiEnable().
+ This is an optional parameter that may be NULL.
+ If this parameter is NULL, then the active periodic
+ SMI handler is returned.
+
+ @retval NULL DispatchHandle is NULL and there is no active periodic SMI
+ handler.
+ @retval NULL DispatchHandle does not match any of the periodic SMI handlers
+ that have been enabled with PeriodicSmiEnable().
+ @retval other Pointer to the PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT
+ associated with the DispatchHandle.
+
+**/
+PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *
+LookupPeriodicSmiLibraryHandler (
+ IN EFI_HANDLE DispatchHandle OPTIONAL
+ )
+{
+ UINTN Index;
+
+ //
+ // If DispatchHandle is NULL, then return the active periodic SMI handler
+ //
+ if (DispatchHandle == NULL) {
+ return GetActivePeriodicSmiLibraryHandler ();
+ }
+
+ //
+ // Search the periodic SMI handler entries for a a matching DispatchHandle
+ //
+ for (Index = 0; Index < gNumberOfPeriodicSmiLibraryHandlers; Index++) {
+ if (gPeriodicSmiLibraryHandlers[Index].DispatchHandle == DispatchHandle) {
+ return &gPeriodicSmiLibraryHandlers[Index];
+ }
+ }
+
+ //
+ // No entries match DispatchHandle, so return NULL
+ //
+ return NULL;
+}
+
+/**
+ Internal worker function that sets that active periodic SMI handler based on
+ the Context used when the periodic SMI handler was registered with the
+ SMM Periodic Timer Dispatch 2 Protocol. If Context is NULL, then the
+ state is updated to show that there is not active periodic SMI handler.
+ A pointer to the active PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT structure
+ is returned.
+
+ @retval NULL Context is NULL.
+ @retval other Pointer to the PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT
+ associated with Context.
+
+**/
+PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *
+SetActivePeriodicSmiLibraryHandler (
+ IN CONST VOID *Context OPTIONAL
+ )
+{
+ PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *PeriodicSmiLibraryHandler;
+
+ if (Context == NULL) {
+ gActivePeriodicSmiLibraryHandlerIndex = -1;
+ return NULL;
+ }
+ PeriodicSmiLibraryHandler = PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT_FROM_REGISTER_CONTEXT (Context);
+ gActivePeriodicSmiLibraryHandlerIndex = PeriodicSmiLibraryHandler - gPeriodicSmiLibraryHandlers;
+ return PeriodicSmiLibraryHandler;
+}
+
+/**
+ Internal worker function that returns a free entry for a new periodic
+ SMI handler. If no free entries are available, then additional
+ entries are allocated.
+
+ @retval NULL There are not enough resources available to to allocate a free entry.
+ @retval other Pointer to a free PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT structure.
+
+**/
+PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *
+FindFreePeriodicSmiLibraryHandler (
+ VOID
+ )
+{
+ UINTN Index;
+ PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *PeriodicSmiLibraryHandler;
+
+ //
+ // Search for a free entry in gPeriodicSmiLibraryHandlers
+ // A free entry must have a NULL DispatchHandle and a NULL Stack.
+ //
+ for (Index = 0; Index < gNumberOfPeriodicSmiLibraryHandlers; Index++) {
+ if (gPeriodicSmiLibraryHandlers[Index].DispatchHandle != NULL) {
+ continue;
+ }
+ if (gPeriodicSmiLibraryHandlers[Index].Stack != NULL) {
+ continue;
+ }
+ return &gPeriodicSmiLibraryHandlers[Index];
+ }
+
+ //
+ // If no free entries were found, then grow the table of periodic SMI handler entries
+ //
+ if (Index == gNumberOfPeriodicSmiLibraryHandlers) {
+ PeriodicSmiLibraryHandler = ReallocatePool (
+ gNumberOfPeriodicSmiLibraryHandlers * sizeof (PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT),
+ (gNumberOfPeriodicSmiLibraryHandlers + PERIODIC_SMI_LIBRARY_ALLOCATE_SIZE) * sizeof (PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT),
+ gPeriodicSmiLibraryHandlers
+ );
+ if (PeriodicSmiLibraryHandler == NULL) {
+ return NULL;
+ }
+ gPeriodicSmiLibraryHandlers = PeriodicSmiLibraryHandler;
+ gNumberOfPeriodicSmiLibraryHandlers += PERIODIC_SMI_LIBRARY_ALLOCATE_SIZE;
+ }
+
+ return &gPeriodicSmiLibraryHandlers[Index];
+}
+
+/**
+ This function returns a pointer to a table of supported periodic
+ SMI tick periods in 100 ns units sorted from largest to smallest.
+ The table contains a array of UINT64 values terminated by a tick
+ period value of 0. The returned table must be treated as read-only
+ data and must not be freed.
+
+ @return A pointer to a table of UINT64 tick period values in
+ 100ns units sorted from largest to smallest terminated
+ by a tick period of 0.
+
+**/
+UINT64 *
+EFIAPI
+PeriodicSmiSupportedTickPeriod (
+ VOID
+ )
+{
+ //
+ // Return the table allocated and populated by SmmPeriodicSmiLibConstructor()
+ //
+ return gSmiTickPeriodTable;
+}
+
+/**
+ This function returns the time in 100ns units since the periodic SMI
+ handler function was called. If the periodic SMI handler was resumed
+ through PeriodicSmiYield(), then the time returned is the time in
+ 100ns units since PeriodicSmiYield() returned.
+
+ @return The actual time in 100ns units that the periodic SMI handler
+ has been executing. If this function is not called from within
+ an enabled periodic SMI handler, then 0 is returned.
+
+**/
+UINT64
+EFIAPI
+PeriodicSmiExecutionTime (
+ VOID
+ )
+{
+ PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *PeriodicSmiLibraryHandler;
+ UINT64 Current;
+ UINT64 Count;
+
+ //
+ // If there is no active periodic SMI handler, then return 0
+ //
+ PeriodicSmiLibraryHandler = GetActivePeriodicSmiLibraryHandler ();
+ if (PeriodicSmiLibraryHandler == NULL) {
+ return 0;
+ }
+
+ //
+ // Get the current performance counter value
+ //
+ Current = GetPerformanceCounter ();
+
+ //
+ // Count the number of performance counter ticks since the periodic SMI handler
+ // was dispatched or the last time this function was called.
+ //
+ if (PeriodicSmiLibraryHandler->PerfomanceCounterEndValue > PeriodicSmiLibraryHandler->PerfomanceCounterStartValue) {
+ //
+ // The performance counter counts up. Check for roll over condition.
+ //
+ if (Current > PeriodicSmiLibraryHandler->DispatchCheckPointTime) {
+ Count = Current - PeriodicSmiLibraryHandler->DispatchCheckPointTime;
+ } else {
+ Count = (Current - PeriodicSmiLibraryHandler->PerfomanceCounterStartValue) + (PeriodicSmiLibraryHandler->PerfomanceCounterEndValue - PeriodicSmiLibraryHandler->DispatchCheckPointTime);
+ }
+ } else {
+ //
+ // The performance counter counts down. Check for roll over condition.
+ //
+ if (PeriodicSmiLibraryHandler->DispatchCheckPointTime > Current) {
+ Count = PeriodicSmiLibraryHandler->DispatchCheckPointTime - Current;
+ } else {
+ Count = (PeriodicSmiLibraryHandler->DispatchCheckPointTime - PeriodicSmiLibraryHandler->PerfomanceCounterEndValue) + (PeriodicSmiLibraryHandler->PerfomanceCounterStartValue - Current);
+ }
+ }
+
+ //
+ // Accumulate the total number of performance counter ticks since the periodic
+ // SMI handler was dispatched or resumed.
+ //
+ PeriodicSmiLibraryHandler->DispatchTotalTime += Count;
+
+ //
+ // Update the checkpoint value to the current performance counter value
+ //
+ PeriodicSmiLibraryHandler->DispatchCheckPointTime = Current;
+
+ //
+ // Convert the total number of performance counter ticks to 100 ns units
+ //
+ return DivU64x64Remainder (
+ MultU64x32 (PeriodicSmiLibraryHandler->DispatchTotalTime, 10000000),
+ PeriodicSmiLibraryHandler->PerfomanceCounterRate,
+ NULL
+ );
+}
+
+/**
+ This function returns control back to the SMM Foundation. When the next
+ periodic SMI for the currently executing handler is triggered, the periodic
+ SMI handler will restarted from its registered DispatchFunction entry point.
+ If this function is not called from within an enabled periodic SMI handler,
+ then control is returned to the calling function.
+
+**/
+VOID
+EFIAPI
+PeriodicSmiExit (
+ VOID
+ )
+{
+ PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *PeriodicSmiLibraryHandler;
+
+ //
+ // If there is no active periodic SMI handler, then return
+ //
+ PeriodicSmiLibraryHandler = GetActivePeriodicSmiLibraryHandler ();
+ if (PeriodicSmiLibraryHandler == NULL) {
+ return;
+ }
+
+ //
+ // Perform a long jump back to the point when the currently executing dispatch
+ // function was dispatched.
+ //
+ LongJump (&PeriodicSmiLibraryHandler->DispatchJumpBuffer, 1);
+
+ //
+ // Must never return
+ //
+ ASSERT (FALSE);
+ CpuDeadLoop();
+}
+
+/**
+ This function yields control back to the SMM Foundation. When the next
+ periodic SMI for the currently executing handler is triggered, the periodic
+ SMI handler will be resumed and this function will return. Use of this
+ function requires a seperate stack for the periodic SMI handler. A non zero
+ stack size must be specified in PeriodicSmiEnable() for this function to be
+ used.
+
+ If the stack size passed into PeriodicSmiEnable() was zero, the 0 is returned.
+
+ If this function is not called from within an enabled periodic SMI handler,
+ then 0 is returned.
+
+ @return The actual time in 100ns units elasped since this function was
+ called. A value of 0 indicates an unknown amount of time.
+
+**/
+UINT64
+EFIAPI
+PeriodicSmiYield (
+ VOID
+ )
+{
+ PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *PeriodicSmiLibraryHandler;
+ UINTN SetJumpFlag;
+
+ //
+ // If there is no active periodic SMI handler, then return
+ //
+ PeriodicSmiLibraryHandler = GetActivePeriodicSmiLibraryHandler ();
+ if (PeriodicSmiLibraryHandler == NULL) {
+ return 0;
+ }
+
+ //
+ // If PeriodicSmiYield() is called without an allocated stack, then just return
+ // immediately with an elapsed time of 0.
+ //
+ if (PeriodicSmiLibraryHandler->Stack == NULL) {
+ return 0;
+ }
+
+ //
+ // Set a flag so the next periodic SMI event will resume at where SetJump()
+ // is called below.
+ //
+ PeriodicSmiLibraryHandler->YieldFlag = TRUE;
+
+ //
+ // Save context in YieldJumpBuffer
+ //
+ SetJumpFlag = SetJump (&PeriodicSmiLibraryHandler->YieldJumpBuffer);
+ if (SetJumpFlag == 0) {
+ //
+ // The intial call to SetJump() always returns 0.
+ // If this is the initial call, then exit the current periodic SMI handler
+ //
+ PeriodicSmiExit ();
+ }
+
+ //
+ // We get here when a LongJump is performed from PeriodicSmiDispatchFunctionOnCpu()
+ // to resume a periodic SMI handler that called PeriodicSmiYield() on the
+ // previous time this periodic SMI handler was dispatched.
+ //
+ // Clear the flag so the next periodic SMI dispatch will not resume.
+ //
+ PeriodicSmiLibraryHandler->YieldFlag = FALSE;
+
+ //
+ // Return the amount elapsed time that occured while yielded
+ //
+ return PeriodicSmiLibraryHandler->ElapsedTime;
+}
+
+/**
+ Internal worker function that transfers control to an enabled periodic SMI
+ handler. If the enabled periodic SMI handler was allocated its own stack,
+ then this function is called on that allocated stack through the BaseLin
+ function SwitchStack().
+
+ @param[in] Context1 Context1 parameter passed into SwitchStack().
+ @param[in] Context2 Context2 parameter passed into SwitchStack().
+
+**/
+VOID
+EFIAPI
+PeriodicSmiDispatchFunctionSwitchStack (
+ IN VOID *Context1, OPTIONAL
+ IN VOID *Context2 OPTIONAL
+ )
+{
+ PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *PeriodicSmiLibraryHandler;
+
+ //
+ // Convert Context1 to PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *
+ //
+ PeriodicSmiLibraryHandler = (PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *)Context1;
+
+ //
+ // Dispatch the registered handler passing in the context that was registered
+ // and the amount of time that has elapsed since the previous time this
+ // periodic SMI handler was dispacthed.
+ //
+ PeriodicSmiLibraryHandler->DispatchFunction (
+ PeriodicSmiLibraryHandler->Context,
+ PeriodicSmiLibraryHandler->ElapsedTime
+ );
+
+ //
+ // If this DispatchFunction() returns, then unconditially call PeriodicSmiExit()
+ // to perform a LongJump() back to PeriodicSmiDispatchFunctionOnCpu(). The
+ // LongJump() will resume exection on the original stack.
+ //
+ PeriodicSmiExit ();
+}
+
+/**
+ Internal worker function that transfers control to an enabled periodic SMI
+ handler on the specified logial CPU. This function determines if the periodic
+ SMI handler yielded and needs to be resumed. It also and switches to an
+ allocated stack if one was allocated in PeriodicSmiEnable().
+
+ @param[in] PeriodicSmiLibraryHandler A pointer to the context for the periodic
+ SMI handler to execute.
+
+**/
+VOID
+EFIAPI
+PeriodicSmiDispatchFunctionOnCpu (
+ PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *PeriodicSmiLibraryHandler
+ )
+{
+ //
+ // Save context in DispatchJumpBuffer. The intial call to SetJump() always
+ // returns 0. If this is the initial call, then either resume from a prior
+ // call to PeriodicSmiYield() or call the DispatchFunction registerd in
+ // PeriodicSmiEnable() using an allocated stack if one was specified.
+ //
+ if (SetJump (&PeriodicSmiLibraryHandler->DispatchJumpBuffer) != 0) {
+ return;
+ }
+
+ //
+ // Capture the performance counter value just before the periodic SMI handler
+ // is resumed so the amount of time the periodic SMI handler executes can be
+ // calculated.
+ //
+ PeriodicSmiLibraryHandler->DispatchTotalTime = 0;
+ PeriodicSmiLibraryHandler->DispatchCheckPointTime = GetPerformanceCounter();
+
+ if (PeriodicSmiLibraryHandler->YieldFlag) {
+ //
+ // Perform a long jump back to the point where the previously dispatched
+ // function called PeriodicSmiYield().
+ //
+ LongJump (&PeriodicSmiLibraryHandler->YieldJumpBuffer, 1);
+ } else if (PeriodicSmiLibraryHandler->Stack == NULL) {
+ //
+ // If Stack is NULL then call DispatchFunction using current stack passing
+ // in the context that was registered and the amount of time that has
+ // elapsed since the previous time this periodic SMI handler was dispacthed.
+ //
+ PeriodicSmiLibraryHandler->DispatchFunction (
+ PeriodicSmiLibraryHandler->Context,
+ PeriodicSmiLibraryHandler->ElapsedTime
+ );
+
+ //
+ // If this DispatchFunction() returns, then unconditially call PeriodicSmiExit()
+ // to perform a LongJump() back to this function.
+ //
+ PeriodicSmiExit ();
+ } else {
+ //
+ // If Stack is not NULL then call DispatchFunction switching to the allocated stack
+ //
+ SwitchStack (
+ PeriodicSmiDispatchFunctionSwitchStack,
+ PeriodicSmiLibraryHandler,
+ NULL,
+ (UINT8 *)PeriodicSmiLibraryHandler->Stack + PeriodicSmiLibraryHandler->StackSize
+ );
+ }
+
+ //
+ // Must never return
+ //
+ ASSERT (FALSE);
+ CpuDeadLoop();
+}
+
+/**
+ Internal worker function that transfers control to an enabled periodic SMI
+ handler on the specified logial CPU. This worker function is only called
+ using the SMM Services Table function SmmStartupThisAp() to execute the
+ periodic SMI handler on a logical CPU that is different than the one that is
+ running the SMM Foundation. When the periodic SMI handler returns, a lock is
+ released to notify the CPU that is running the SMM Foundation that the periodic
+ SMI handler execution has finished its execution.
+
+ @param[in] Buffer A pointer to the context for the periodic SMI handler.
+
+**/
+VOID
+EFIAPI
+PeriodicSmiDispatchFunctionWithLock (
+ IN OUT VOID *Buffer
+ )
+{
+ PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *PeriodicSmiLibraryHandler;
+
+ //
+ // Get context
+ //
+ PeriodicSmiLibraryHandler = (PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *)Buffer;
+
+ //
+ // Execute dispatch function on the currently excuting logical CPU
+ //
+ PeriodicSmiDispatchFunctionOnCpu (PeriodicSmiLibraryHandler);
+
+ //
+ // Release the dispatch spin lock
+ //
+ ReleaseSpinLock (&PeriodicSmiLibraryHandler->DispatchLock);
+}
+
+/**
+ Internal worker function that transfers control to a periodic SMI handler that
+ was enabled using PeriodicSmiEnable().
+
+ @param[in] DispatchHandle The unique handle assigned to this handler by
+ SmiHandlerRegister().
+ @param[in] Context Points to an optional handler context which was
+ specified when the handler was registered.
+ @param[in,out] CommBuffer A pointer to a collection of data in memory that
+ will be conveyed from a non-SMM environment into
+ an SMM environment.
+ @param[in,out] CommBufferSize The size of the CommBuffer.
+
+ @retval EFI_SUCCESS The interrupt was handled and quiesced.
+ No other handlers should still be called.
+ @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other
+ handlers should still be called.
+ @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other
+ handlers should still be called.
+ @retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced.
+
+**/
+EFI_STATUS
+EFIAPI
+PeriodicSmiDispatchFunction (
+ IN EFI_HANDLE DispatchHandle,
+ IN CONST VOID *Context OPTIONAL,
+ IN OUT VOID *CommBuffer OPTIONAL,
+ IN OUT UINTN *CommBufferSize OPTIONAL
+ )
+{
+ PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *PeriodicSmiLibraryHandler;
+ EFI_SMM_PERIODIC_TIMER_CONTEXT *TimerContext;
+ EFI_STATUS Status;
+
+ //
+ // Set the active periodic SMI handler
+ //
+ PeriodicSmiLibraryHandler = SetActivePeriodicSmiLibraryHandler (Context);
+ if (PeriodicSmiLibraryHandler == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Retrieve the elapsed time since the last time this periodic SMI handler was called
+ //
+ PeriodicSmiLibraryHandler->ElapsedTime = 0;
+ if (CommBuffer != NULL) {
+ TimerContext = (EFI_SMM_PERIODIC_TIMER_CONTEXT *)CommBuffer;
+ PeriodicSmiLibraryHandler->ElapsedTime = TimerContext->ElapsedTime;
+ }
+
+ //
+ // Dispatch the periodic SMI handler
+ //
+ if ((PeriodicSmiLibraryHandler->Cpu == PERIODIC_SMI_LIBRARY_ANY_CPU) ||
+ (PeriodicSmiLibraryHandler->Cpu == gSmst->CurrentlyExecutingCpu) ) {
+ //
+ // Dispatch on the currently execution CPU if the CPU specified in PeriodicSmiEnable()
+ // was PERIODIC_SMI_LIBARRY_ANY_CPU or the currently executing CPU matches the CPU
+ // that was specified in PeriodicSmiEnable().
+ //
+ PeriodicSmiDispatchFunctionOnCpu (PeriodicSmiLibraryHandler);
+ } else {
+ //
+ // Acquire spin lock for ths periodic SMI handler. The AP will release the
+ // spin lock when it is done executing the periodic SMI handler.
+ //
+ AcquireSpinLock (&PeriodicSmiLibraryHandler->DispatchLock);
+
+ //
+ // Execute the periodic SMI handler on the CPU that was specified in
+ // PeriodicSmiEnable().
+ //
+ Status = gSmst->SmmStartupThisAp (
+ PeriodicSmiDispatchFunctionWithLock,
+ PeriodicSmiLibraryHandler->Cpu,
+ PeriodicSmiLibraryHandler
+ );
+ if (!EFI_ERROR (Status)) {
+ //
+ // Wait for the AP to release the spin lock.
+ //
+ while (!AcquireSpinLockOrFail (&PeriodicSmiLibraryHandler->DispatchLock)) {
+ CpuPause ();
+ }
+ }
+
+ //
+ // Release the spin lock for the periodic SMI handler.
+ //
+ ReleaseSpinLock (&PeriodicSmiLibraryHandler->DispatchLock);
+ }
+
+ //
+ // Retrieve the active periodic SMI handler in case the entries were reallocated
+ // when the active periodic SMI handler was dispatched.
+ //
+ PeriodicSmiLibraryHandler = GetActivePeriodicSmiLibraryHandler ();
+ if (PeriodicSmiLibraryHandler != NULL) {
+ //
+ // If the active periodic SMI handler was disabled during the current dispatch
+ // and the periodic SMI handler was allocated a stack when it was enabled, then
+ // free that stack here.
+ //
+ if (PeriodicSmiLibraryHandler->DispatchHandle == NULL) {
+ if (PeriodicSmiLibraryHandler->Stack != NULL) {
+ FreePages (
+ PeriodicSmiLibraryHandler->Stack,
+ EFI_SIZE_TO_PAGES (PeriodicSmiLibraryHandler->StackSize)
+ );
+ PeriodicSmiLibraryHandler->Stack = NULL;
+ }
+ }
+ }
+
+ //
+ // Update state to show that there is no active periodic SMI handler
+ //
+ SetActivePeriodicSmiLibraryHandler (NULL);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function enables a periodic SMI handler.
+
+ @param[in,out] DispatchHandle A pointer to the handle associated with the
+ enabled periodic SMI handler. This is an
+ optional parameter that may be NULL. If it is
+ NULL, then the handle will not be returned,
+ which means that the periodic SMI handler can
+ never be disabled.
+ @param[in] DispatchFunction A pointer to a periodic SMI handler function.
+ @param[in] Context Optional content to pass into DispatchFunction.
+ @param[in] TickPeriod The requested tick period in 100ns units that
+ control should be givien to the periodic SMI
+ handler. Must be one of the supported values
+ returned by PeriodicSmiSupportedPickPeriod().
+ @param[in] Cpu Specifies the CPU that is required to execute
+ the periodic SMI handler. If Cpu is
+ PERIODIC_SMI_LIBRARY_ANY_CPU, then the periodic
+ SMI handler will always be executed on the SMST
+ CurrentlyExecutingCpu, which may vary across
+ periodic SMIs. If Cpu is between 0 and the SMST
+ NumberOfCpus, then the periodic SMI will always
+ be executed on the requested CPU.
+ @param[in] StackSize The size, in bytes, of the stack to allocate for
+ use by the periodic SMI handler. If 0, then the
+ default stack will be used.
+
+ @retval EFI_INVALID_PARAMETER DispatchFunction is NULL.
+ @retval EFI_UNSUPPORTED TickPeriod is not a supported tick period. The
+ supported tick periods can be retrieved using
+ PeriodicSmiSupportedTickPeriod().
+ @retval EFI_INVALID_PARAMETER Cpu is not PERIODIC_SMI_LIBRARY_ANY_CPU or in
+ the range 0 to SMST NumberOfCpus.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to enable the
+ periodic SMI handler.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate the
+ stack speficied by StackSize.
+ @retval EFI_SUCCESS The periodic SMI handler was enabled.
+
+**/
+EFI_STATUS
+EFIAPI
+PeriodicSmiEnable (
+ IN OUT EFI_HANDLE *DispatchHandle, OPTIONAL
+ IN PERIODIC_SMI_LIBRARY_HANDLER DispatchFunction,
+ IN CONST VOID *Context, OPTIONAL
+ IN UINT64 TickPeriod,
+ IN UINTN Cpu,
+ IN UINTN StackSize
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *PeriodicSmiLibraryHandler;
+
+ //
+ // Make sure all the input parameters are valid
+ //
+ if (DispatchFunction == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ for (Index = 0; gSmiTickPeriodTable[Index] != 0; Index++) {
+ if (gSmiTickPeriodTable[Index] == TickPeriod) {
+ break;
+ }
+ }
+ if (gSmiTickPeriodTable[Index] == 0) {
+ return EFI_UNSUPPORTED;
+ }
+
+ if (Cpu != PERIODIC_SMI_LIBRARY_ANY_CPU && Cpu >= gSmst->NumberOfCpus) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Find a free periodic SMI handler entry
+ //
+ PeriodicSmiLibraryHandler = FindFreePeriodicSmiLibraryHandler();
+ if (PeriodicSmiLibraryHandler == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Initialize a new periodic SMI handler entry
+ //
+ PeriodicSmiLibraryHandler->Signature = PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT_SIGNATURE;
+ PeriodicSmiLibraryHandler->YieldFlag = FALSE;
+ PeriodicSmiLibraryHandler->DispatchHandle = NULL;
+ PeriodicSmiLibraryHandler->DispatchFunction = DispatchFunction;
+ PeriodicSmiLibraryHandler->Context = (VOID *)Context;
+ PeriodicSmiLibraryHandler->Cpu = Cpu;
+ PeriodicSmiLibraryHandler->StackSize = ALIGN_VALUE (StackSize, EFI_PAGE_SIZE);
+ if (PeriodicSmiLibraryHandler->StackSize > 0) {
+ PeriodicSmiLibraryHandler->Stack = AllocatePages (EFI_SIZE_TO_PAGES (PeriodicSmiLibraryHandler->StackSize));
+ if (PeriodicSmiLibraryHandler->Stack == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ ZeroMem (PeriodicSmiLibraryHandler->Stack, PeriodicSmiLibraryHandler->StackSize);
+ }
+ InitializeSpinLock (&PeriodicSmiLibraryHandler->DispatchLock);
+ PeriodicSmiLibraryHandler->PerfomanceCounterRate = GetPerformanceCounterProperties (
+ &PeriodicSmiLibraryHandler->PerfomanceCounterStartValue,
+ &PeriodicSmiLibraryHandler->PerfomanceCounterEndValue
+ );
+ PeriodicSmiLibraryHandler->RegisterContext.Period = TickPeriod;
+ PeriodicSmiLibraryHandler->RegisterContext.SmiTickInterval = TickPeriod;
+ Status = gSmmPeriodicTimerDispatch2->Register (
+ gSmmPeriodicTimerDispatch2,
+ PeriodicSmiDispatchFunction,
+ &PeriodicSmiLibraryHandler->RegisterContext,
+ &PeriodicSmiLibraryHandler->DispatchHandle
+ );
+ if (EFI_ERROR (Status) || PeriodicSmiLibraryHandler->DispatchHandle == NULL) {
+ //
+ // If the registration failed or the handle is invalid, free the stack if one was allocated
+ //
+ if (PeriodicSmiLibraryHandler->Stack != NULL) {
+ FreePages (
+ PeriodicSmiLibraryHandler->Stack,
+ EFI_SIZE_TO_PAGES (PeriodicSmiLibraryHandler->StackSize)
+ );
+ PeriodicSmiLibraryHandler->Stack = NULL;
+ }
+ PeriodicSmiLibraryHandler->DispatchHandle = NULL;
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Return the registered handle if the optional DispatchHandle parameter is not NULL
+ //
+ if (DispatchHandle != NULL) {
+ *DispatchHandle = PeriodicSmiLibraryHandler->DispatchHandle;
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ This function disables a periodic SMI handler that has been previously
+ enabled with PeriodicSmiEnable().
+
+ @param[in] DispatchHandle A handle associated with a previously enabled periodic
+ SMI handler. This is an optional parameter that may
+ be NULL. If it is NULL, then the active periodic SMI
+ handlers is disabled.
+
+ @retval FALSE DispatchHandle is NULL and there is no active periodic SMI handler.
+ @retval FALSE The periodic SMI handler specified by DispatchHandle has
+ not been enabled with PeriodicSmiEnable().
+ @retval TRUE The periodic SMI handler specified by DispatchHandle has
+ been disabled. If DispatchHandle is NULL, then the active
+ periodic SMI handler has been disabled.
+
+**/
+BOOLEAN
+EFIAPI
+PeriodicSmiDisable (
+ IN EFI_HANDLE DispatchHandle OPTIONAL
+ )
+{
+ PERIODIC_SMI_LIBRARY_HANDLER_CONTEXT *PeriodicSmiLibraryHandler;
+ EFI_STATUS Status;
+
+ //
+ // Lookup the periodic SMI handler specified by DispatchHandle
+ //
+ PeriodicSmiLibraryHandler = LookupPeriodicSmiLibraryHandler (DispatchHandle);
+ if (PeriodicSmiLibraryHandler == NULL) {
+ return FALSE;
+ }
+
+ //
+ // Unregister the periodic SMI handler from the SMM Periodic Timer Dispatch 2 Protocol
+ //
+ Status = gSmmPeriodicTimerDispatch2->UnRegister (
+ gSmmPeriodicTimerDispatch2,
+ PeriodicSmiLibraryHandler->DispatchHandle
+ );
+ if (EFI_ERROR (Status)) {
+ return FALSE;
+ }
+
+ //
+ // If active periodic SMI handler is not the periodic SMI handler being
+ // disabled, and the periodic SMI handler being disabled was allocated a
+ // stack when it was enabled, then free the stack.
+ //
+ if (PeriodicSmiLibraryHandler != GetActivePeriodicSmiLibraryHandler ()) {
+ if (PeriodicSmiLibraryHandler->Stack != NULL) {
+ FreePages (
+ PeriodicSmiLibraryHandler->Stack,
+ EFI_SIZE_TO_PAGES (PeriodicSmiLibraryHandler->StackSize)
+ );
+ PeriodicSmiLibraryHandler->Stack = NULL;
+ }
+ }
+
+ //
+ // Mark the entry for the disabled periodic SMI handler as free
+ //
+ PeriodicSmiLibraryHandler->DispatchHandle = NULL;
+
+ return TRUE;
+}
+
+/**
+ This constructor function caches the pointer to the SMM Periodic Timer
+ Dispatch 2 Protocol and collects the list SMI tick rates that the hardware
+ supports.
+
+ @param[in] ImageHandle The firmware allocated handle for the EFI image.
+ @param[in] SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS.
+
+**/
+EFI_STATUS
+EFIAPI
+SmmPeriodicSmiLibConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ UINT64 *SmiTickInterval;
+ UINTN Count;
+
+ //
+ // Locate the SMM Periodic Timer Dispatch 2 Protocol
+ //
+ Status = gSmst->SmmLocateProtocol (
+ &gEfiSmmPeriodicTimerDispatch2ProtocolGuid,
+ NULL,
+ (VOID **)&gSmmPeriodicTimerDispatch2
+ );
+ ASSERT_EFI_ERROR (Status);
+ ASSERT (gSmmPeriodicTimerDispatch2 != NULL);
+
+ //
+ // Count the number of periodic SMI tick intervals that the SMM Periodic Timer
+ // Dipatch 2 Protocol supports.
+ //
+ SmiTickInterval = NULL;
+ Count = 0;
+ do {
+ Status = gSmmPeriodicTimerDispatch2->GetNextShorterInterval (
+ gSmmPeriodicTimerDispatch2,
+ &SmiTickInterval
+ );
+ Count++;
+ } while (SmiTickInterval != NULL);
+
+ //
+ // Allocate a buffer for the table of supported periodic SMI tick periods.
+ //
+ gSmiTickPeriodTable = AllocateZeroPool (Count * sizeof (UINT64));
+ ASSERT (gSmiTickPeriodTable != NULL);
+
+ //
+ // Fill in the table of supported periodic SMI tick periods.
+ //
+ SmiTickInterval = NULL;
+ Count = 0;
+ do {
+ gSmiTickPeriodTable[Count] = 0;
+ Status = gSmmPeriodicTimerDispatch2->GetNextShorterInterval (
+ gSmmPeriodicTimerDispatch2,
+ &SmiTickInterval
+ );
+ if (SmiTickInterval != NULL) {
+ gSmiTickPeriodTable[Count] = *SmiTickInterval;
+ }
+ Count++;
+ } while (SmiTickInterval != NULL);
+
+ //
+ // Allocate buffer for initial set of periodic SMI handlers
+ //
+ FindFreePeriodicSmiLibraryHandler ();
+
+ return EFI_SUCCESS;
+}
+
+/**
+ The constructor function caches the pointer to the SMM Periodic Timer Dispatch 2
+ Protocol and collects the list SMI tick rates that the hardware supports.
+
+ @param[in] ImageHandle The firmware allocated handle for the EFI image.
+ @param[in] SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS.
+
+**/
+EFI_STATUS
+EFIAPI
+SmmPeriodicSmiLibDestructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ UINTN Index;
+
+ //
+ // Free the table of supported periodic SMI tick rates
+ //
+ if (gSmiTickPeriodTable != NULL) {
+ FreePool (gSmiTickPeriodTable);
+ }
+
+ //
+ // Disable all periodic SMI handlers
+ //
+ for (Index = 0; Index < gNumberOfPeriodicSmiLibraryHandlers; Index++) {
+ PeriodicSmiDisable (gPeriodicSmiLibraryHandlers[Index].DispatchHandle);
+ }
+
+ //
+ // Free all the periodic SMI handler entries
+ //
+ if (gPeriodicSmiLibraryHandlers != NULL) {
+ FreePool (gPeriodicSmiLibraryHandlers);
+ }
+
+ return EFI_SUCCESS;
+}
diff --git a/MdePkg/Library/SmmPeriodicSmiLib/SmmPeriodicSmiLib.inf b/MdePkg/Library/SmmPeriodicSmiLib/SmmPeriodicSmiLib.inf
new file mode 100644
index 000000000..2acb59afa
--- /dev/null
+++ b/MdePkg/Library/SmmPeriodicSmiLib/SmmPeriodicSmiLib.inf
@@ -0,0 +1,50 @@
+## @file
+# SMM Periodic SMI Library.
+#
+# Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>
+#
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php.
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = SmmPeriodicSmiLib
+ FILE_GUID = AED5F3FB-4CFF-4b60-9E43-1541B55C8267
+ MODULE_TYPE = DXE_SMM_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = SmmPeriodicSmiLib|DXE_SMM_DRIVER
+ PI_SPECIFICATION_VERSION = 0x0001000A
+ CONSTRUCTOR = SmmPeriodicSmiLibConstructor
+ DESTRUCTOR = SmmPeriodicSmiLibDestructor
+
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[Sources]
+ SmmPeriodicSmiLib.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ SynchronizationLib
+ DebugLib
+ TimerLib
+ MemoryAllocationLib
+ SmmServicesTableLib
+
+[Protocols]
+ gEfiSmmPeriodicTimerDispatch2ProtocolGuid
+
+[Depex]
+ gEfiSmmPeriodicTimerDispatch2ProtocolGuid
diff --git a/MdePkg/MdePkg.dec b/MdePkg/MdePkg.dec
index cdcab950c..60c8a3cc8 100644
--- a/MdePkg/MdePkg.dec
+++ b/MdePkg/MdePkg.dec
@@ -5,7 +5,7 @@
# It also provides the definitions(including PPIs/PROTOCOLs/GUIDs) of
# EFI1.10/UEFI2.3/PI1.2 and some Industry Standards.
#
-# Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2007 - 2011, Intel Corporation. All rights reserved.<BR>
# Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
#
# This program and the accompanying materials are licensed and made available under
@@ -200,6 +200,10 @@
# Only available to SMM/DXE Combined and SMM module types.
SmmServicesTableLib|Include/Library/SmmServicesTableLib.h
+ ## @libraryclass Provides services to enable/disable periodic SMI handlers.
+ #
+ SmmPeriodicSmiLib|Include/Library/SmmPeriodicSmiLib.h
+
[LibraryClasses.IPF]
## @libraryclass The SAL Library provides a service to make a SAL CALL.
SalLib|Include/Library/SalLib.h
diff --git a/MdePkg/MdePkg.dsc b/MdePkg/MdePkg.dsc
index 04242b2b2..9ca57b840 100644
--- a/MdePkg/MdePkg.dsc
+++ b/MdePkg/MdePkg.dsc
@@ -1,7 +1,7 @@
## @file
# EFI/PI MdePkg Package
#
-# Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>
+# Copyright (c) 2007 - 2011, Intel Corporation. All rights reserved.<BR>
# Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
#
# This program and the accompanying materials
@@ -139,6 +139,7 @@
MdePkg/Library/SmmPciLibPciRootBridgeIo/SmmPciLibPciRootBridgeIo.inf
MdePkg/Library/SmmServicesTableLib/SmmServicesTableLib.inf
MdePkg/Library/SmmMemoryAllocationLib/SmmMemoryAllocationLib.inf
+ MdePkg/Library/SmmPeriodicSmiLib/SmmPeriodicSmiLib.inf
[Components.IPF]
MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf
@@ -149,7 +150,6 @@
MdePkg/Library/UefiPalLib/UefiPalLib.inf
MdePkg/Library/UefiSalLib/UefiSalLib.inf
-
[Components.EBC]
MdePkg/Library/UefiRuntimeLib/UefiRuntimeLib.inf