2011-02-11 01:09:16 +01:00
|
|
|
/** @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.
|
|
|
|
|
2011-02-17 01:45:08 +01:00
|
|
|
@param[in, out] Buffer A pointer to the context for the periodic SMI handler.
|
2011-02-11 01:09:16 +01:00
|
|
|
|
|
|
|
**/
|
|
|
|
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.
|
2011-02-17 01:45:08 +01:00
|
|
|
@param[in, out] CommBuffer A pointer to a collection of data in memory that
|
2011-02-11 01:09:16 +01:00
|
|
|
will be conveyed from a non-SMM environment into
|
|
|
|
an SMM environment.
|
2011-02-17 01:45:08 +01:00
|
|
|
@param[in, out] CommBufferSize The size of the CommBuffer.
|
2011-02-11 01:09:16 +01:00
|
|
|
|
|
|
|
@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.
|
|
|
|
|
2011-02-17 01:45:08 +01:00
|
|
|
@param[in, out] DispatchHandle A pointer to the handle associated with the
|
2011-02-11 01:09:16 +01:00
|
|
|
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;
|
|
|
|
}
|