410 lines
14 KiB
C
410 lines
14 KiB
C
/** @file
|
|
File to contain all the hardware specific stuff for the Smm Sw dispatch protocol.
|
|
|
|
@copyright
|
|
INTEL CONFIDENTIAL
|
|
Copyright 1999 - 2018 Intel Corporation.
|
|
|
|
The source code contained or described herein and all documents related to the
|
|
source code ("Material") are owned by Intel Corporation or its suppliers or
|
|
licensors. Title to the Material remains with Intel Corporation or its suppliers
|
|
and licensors. The Material may contain trade secrets and proprietary and
|
|
confidential information of Intel Corporation and its suppliers and licensors,
|
|
and is protected by worldwide copyright and trade secret laws and treaty
|
|
provisions. No part of the Material may be used, copied, reproduced, modified,
|
|
published, uploaded, posted, transmitted, distributed, or disclosed in any way
|
|
without Intel's prior express written permission.
|
|
|
|
No license under any patent, copyright, trade secret or other intellectual
|
|
property right is granted to or conferred upon you by disclosure or delivery
|
|
of the Materials, either expressly, by implication, inducement, estoppel or
|
|
otherwise. Any license under such intellectual property rights must be
|
|
express and approved by Intel in writing.
|
|
|
|
Unless otherwise agreed by Intel in writing, you may not remove or alter
|
|
this notice or any other notice embedded in Materials by Intel or
|
|
Intel's suppliers or licensors in any way.
|
|
|
|
This file contains an 'Intel Peripheral Driver' and is uniquely identified as
|
|
"Intel Reference Module" and is licensed for Intel CPUs and chipsets under
|
|
the terms of your license agreement with Intel or your vendor. This file may
|
|
be modified by the user, subject to additional terms of the license agreement.
|
|
|
|
@par Specification Reference:
|
|
**/
|
|
#include "PchSmmHelpers.h"
|
|
#include <Protocol/SmmCpu.h>
|
|
#include <Register/PchRegsLpc.h>
|
|
#include <Register/PmcRegs.h>
|
|
|
|
GLOBAL_REMOVE_IF_UNREFERENCED EFI_SMM_CPU_PROTOCOL *mSmmCpuProtocol;
|
|
|
|
STATIC LIST_ENTRY mSwSmiCallbackDataBase;
|
|
|
|
//
|
|
// "SWSMI" RECORD
|
|
// Linked list data structures
|
|
//
|
|
#define SW_SMI_RECORD_SIGNATURE SIGNATURE_32 ('S', 'W', 'S', 'M')
|
|
|
|
#define SW_SMI_RECORD_FROM_LINK(_record) CR (_record, SW_SMI_RECORD, Link, SW_SMI_RECORD_SIGNATURE)
|
|
|
|
typedef struct {
|
|
UINT32 Signature;
|
|
LIST_ENTRY Link;
|
|
EFI_SMM_SW_REGISTER_CONTEXT Context;
|
|
EFI_SMM_HANDLER_ENTRY_POINT2 Callback;
|
|
} SW_SMI_RECORD;
|
|
|
|
GLOBAL_REMOVE_IF_UNREFERENCED CONST PCH_SMM_SOURCE_DESC mSwSourceDesc = {
|
|
PCH_SMM_NO_FLAGS,
|
|
{
|
|
{
|
|
{
|
|
ACPI_ADDR_TYPE,
|
|
{R_ACPI_IO_SMI_EN}
|
|
},
|
|
S_ACPI_IO_SMI_EN,
|
|
N_ACPI_IO_SMI_EN_APMC
|
|
},
|
|
NULL_BIT_DESC_INITIALIZER
|
|
},
|
|
{
|
|
{
|
|
{
|
|
ACPI_ADDR_TYPE,
|
|
{R_ACPI_IO_SMI_STS}
|
|
},
|
|
S_ACPI_IO_SMI_STS,
|
|
N_ACPI_IO_SMI_STS_APM
|
|
}
|
|
},
|
|
{
|
|
{
|
|
ACPI_ADDR_TYPE,
|
|
{R_ACPI_IO_SMI_STS}
|
|
},
|
|
S_ACPI_IO_SMI_STS,
|
|
N_ACPI_IO_SMI_STS_APM
|
|
}
|
|
};
|
|
|
|
/**
|
|
Check the SwSmiInputValue to see if there is a duplicated one in the database
|
|
|
|
@param[in] SwSmiInputValue SwSmiInputValue
|
|
|
|
@retval EFI_SUCCESS There is no duplicated SwSmiInputValue
|
|
@retval EFI_INVALID_PARAMETER There is a duplicated SwSmiInputValue
|
|
**/
|
|
EFI_STATUS
|
|
SmiInputValueDuplicateCheck (
|
|
IN UINTN SwSmiInputValue
|
|
)
|
|
{
|
|
SW_SMI_RECORD *SwSmiRecord;
|
|
LIST_ENTRY *LinkInDb;
|
|
|
|
LinkInDb = GetFirstNode (&mSwSmiCallbackDataBase);
|
|
while (!IsNull (&mSwSmiCallbackDataBase, LinkInDb)) {
|
|
SwSmiRecord = SW_SMI_RECORD_FROM_LINK (LinkInDb);
|
|
if (SwSmiRecord->Context.SwSmiInputValue == SwSmiInputValue) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
LinkInDb = GetNextNode (&mSwSmiCallbackDataBase, &SwSmiRecord->Link);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Register a child SMI source dispatch function for the specified software SMI.
|
|
|
|
This service registers a function (DispatchFunction) which will be called when the software
|
|
SMI source specified by RegisterContext->SwSmiCpuIndex is detected. On return,
|
|
DispatchHandle contains a unique handle which may be used later to unregister the function
|
|
using UnRegister().
|
|
|
|
@param[in] This Pointer to the EFI_SMM_SW_DISPATCH2_PROTOCOL instance.
|
|
@param[in] DispatchFunction Function to register for handler when the specified software
|
|
SMI is generated.
|
|
@param[in, out] RegisterContext Pointer to the dispatch function's context.
|
|
The caller fills this context in before calling
|
|
the register function to indicate to the register
|
|
function which Software SMI input value the
|
|
dispatch function should be invoked for.
|
|
@param[out] DispatchHandle Handle generated by the dispatcher to track the
|
|
function instance.
|
|
|
|
@retval EFI_SUCCESS The dispatch function has been successfully
|
|
registered and the SMI source has been enabled.
|
|
@retval EFI_DEVICE_ERROR The SW driver was unable to enable the SMI source.
|
|
@retval EFI_INVALID_PARAMETER RegisterContext is invalid. The SW SMI input value
|
|
is not within a valid range or is already in use.
|
|
@retval EFI_OUT_OF_RESOURCES There is not enough memory (system or SMM) to manage this
|
|
child.
|
|
@retval EFI_OUT_OF_RESOURCES A unique software SMI value could not be assigned
|
|
for this dispatch.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
PchSwSmiRegister (
|
|
IN EFI_SMM_SW_DISPATCH2_PROTOCOL *This,
|
|
IN EFI_SMM_HANDLER_ENTRY_POINT2 DispatchFunction,
|
|
IN EFI_SMM_SW_REGISTER_CONTEXT *DispatchContext,
|
|
OUT EFI_HANDLE *DispatchHandle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
SW_SMI_RECORD *SwSmiRecord;
|
|
UINTN Index;
|
|
|
|
//
|
|
// Return access denied if the SmmReadyToLock event has been triggered
|
|
//
|
|
if (mReadyToLock == TRUE) {
|
|
DEBUG ((DEBUG_ERROR, "Register is not allowed if the SmmReadyToLock event has been triggered! \n"));
|
|
return EFI_ACCESS_DENIED;
|
|
}
|
|
|
|
//
|
|
// Find available SW SMI value if the input is -1
|
|
//
|
|
if (DispatchContext->SwSmiInputValue == (UINTN) -1) {
|
|
for (Index = 1; Index < MAXIMUM_SWI_VALUE; Index++) {
|
|
if (!EFI_ERROR (SmiInputValueDuplicateCheck (Index))) {
|
|
DispatchContext->SwSmiInputValue = Index;
|
|
break;
|
|
}
|
|
}
|
|
if (DispatchContext->SwSmiInputValue == (UINTN) -1) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
}
|
|
//
|
|
// Check if it's a valid SW SMI value.
|
|
// The value must not bigger than 0xFF.
|
|
// And the value must not be 0xFF sincie it's used for SmmControll protocol.
|
|
//
|
|
if (DispatchContext->SwSmiInputValue >= MAXIMUM_SWI_VALUE) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (EFI_ERROR (SmiInputValueDuplicateCheck (DispatchContext->SwSmiInputValue))) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Create database record and add to database
|
|
//
|
|
Status = gSmst->SmmAllocatePool (
|
|
EfiRuntimeServicesData,
|
|
sizeof (SW_SMI_RECORD),
|
|
(VOID **) &SwSmiRecord
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_ERROR, "Failed to allocate memory for SwSmiRecord! \n"));
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
//
|
|
// Gather information about the registration request
|
|
//
|
|
SwSmiRecord->Signature = SW_SMI_RECORD_SIGNATURE;
|
|
SwSmiRecord->Context.SwSmiInputValue = DispatchContext->SwSmiInputValue;
|
|
SwSmiRecord->Callback = DispatchFunction;
|
|
//
|
|
// Publish the S/W SMI numbers in Serial logs used for Debug build.
|
|
//
|
|
DEBUG ((DEBUG_INFO, "SW SMI NUM %x Sw Record at Address 0x%X\n", SwSmiRecord->Context.SwSmiInputValue, SwSmiRecord));
|
|
|
|
InsertTailList (&mSwSmiCallbackDataBase, &SwSmiRecord->Link);
|
|
|
|
//
|
|
// Child's handle will be the address linked list link in the record
|
|
//
|
|
*DispatchHandle = (EFI_HANDLE) (&SwSmiRecord->Link);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Unregister a child SMI source dispatch function for the specified software SMI.
|
|
|
|
This service removes the handler associated with DispatchHandle so that it will no longer be
|
|
called in response to a software SMI.
|
|
|
|
@param[in] This Pointer to the EFI_SMM_SW_DISPATCH2_PROTOCOL instance.
|
|
@param[in] DispatchHandle Handle of dispatch function to deregister.
|
|
|
|
@retval EFI_SUCCESS The dispatch function has been successfully unregistered.
|
|
@retval EFI_INVALID_PARAMETER The DispatchHandle was not valid.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
PchSwSmiUnRegister (
|
|
IN CONST EFI_SMM_SW_DISPATCH2_PROTOCOL *This,
|
|
IN EFI_HANDLE DispatchHandle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
SW_SMI_RECORD *RecordToDelete;
|
|
|
|
if (DispatchHandle == 0) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Return access denied if the SmmReadyToLock event has been triggered
|
|
//
|
|
if (mReadyToLock == TRUE) {
|
|
DEBUG ((DEBUG_ERROR, "UnRegister is not allowed if the SmmReadyToLock event has been triggered! \n"));
|
|
return EFI_ACCESS_DENIED;
|
|
}
|
|
|
|
RecordToDelete = SW_SMI_RECORD_FROM_LINK (DispatchHandle);
|
|
//
|
|
// Take the entry out of the linked list
|
|
//
|
|
if (RecordToDelete->Signature != SW_SMI_RECORD_SIGNATURE) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
RemoveEntryList (&RecordToDelete->Link);
|
|
ZeroMem (RecordToDelete, sizeof (SW_SMI_RECORD));
|
|
Status = gSmst->SmmFreePool (RecordToDelete);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Main entry point for an SMM handler dispatch or communicate-based callback.
|
|
|
|
@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
|
|
PchSwSmiDispatcher (
|
|
IN EFI_HANDLE DispatchHandle,
|
|
IN CONST VOID *Context,
|
|
IN OUT VOID *CommBuffer,
|
|
IN OUT UINTN *CommBufferSize
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_SMM_SAVE_STATE_IO_INFO SmiIoInfo;
|
|
UINTN CpuIndex;
|
|
SW_SMI_RECORD *SwSmiRecord;
|
|
LIST_ENTRY *LinkInDb;
|
|
EFI_SMM_SW_CONTEXT SwSmiCommBuffer;
|
|
UINTN SwSmiCommBufferSize;
|
|
|
|
SwSmiCommBufferSize = sizeof (EFI_SMM_SW_CONTEXT);
|
|
//
|
|
// The value in DataPort might not be accurate in multiple thread environment.
|
|
// There might be racing condition for R_PCH_IO_APM_STS port.
|
|
// Therefor, this is just for reference.
|
|
//
|
|
SwSmiCommBuffer.DataPort = IoRead8 (R_PCH_IO_APM_STS);
|
|
|
|
for (CpuIndex = 0; CpuIndex < gSmst->NumberOfCpus; CpuIndex++) {
|
|
Status = mSmmCpuProtocol->ReadSaveState (
|
|
mSmmCpuProtocol,
|
|
sizeof (EFI_SMM_SAVE_STATE_IO_INFO),
|
|
EFI_SMM_SAVE_STATE_REGISTER_IO,
|
|
CpuIndex,
|
|
&SmiIoInfo
|
|
);
|
|
//
|
|
// If this is not the SMI source, skip it.
|
|
//
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
//
|
|
// If the IO address is not "BYTE" "WRITE" to "R_PCH_IO_APM_CNT (0xB2)", skip it.
|
|
//
|
|
if ((SmiIoInfo.IoPort != R_PCH_IO_APM_CNT) ||
|
|
(SmiIoInfo.IoType != EFI_SMM_SAVE_STATE_IO_TYPE_OUTPUT) ||
|
|
(SmiIoInfo.IoWidth != EFI_SMM_SAVE_STATE_IO_WIDTH_UINT8))
|
|
{
|
|
continue;
|
|
}
|
|
//
|
|
// If the IO data is used for SmmControl protocol, skip it.
|
|
//
|
|
if (SmiIoInfo.IoData == 0xFF) {
|
|
continue;
|
|
}
|
|
|
|
SwSmiCommBuffer.SwSmiCpuIndex = CpuIndex;
|
|
SwSmiCommBuffer.CommandPort = (UINT8) SmiIoInfo.IoData;
|
|
|
|
LinkInDb = GetFirstNode (&mSwSmiCallbackDataBase);
|
|
while (!IsNull (&mSwSmiCallbackDataBase, LinkInDb)) {
|
|
SwSmiRecord = SW_SMI_RECORD_FROM_LINK (LinkInDb);
|
|
if (SwSmiRecord->Context.SwSmiInputValue == SmiIoInfo.IoData) {
|
|
SwSmiRecord->Callback ((EFI_HANDLE) &SwSmiRecord->Link, &SwSmiRecord->Context, &SwSmiCommBuffer, &SwSmiCommBufferSize);
|
|
}
|
|
LinkInDb = GetNextNode (&mSwSmiCallbackDataBase, &SwSmiRecord->Link);
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Init required protocol for Pch Sw Dispatch protocol.
|
|
**/
|
|
VOID
|
|
PchSwDispatchInit (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_HANDLE DispatchHandle;
|
|
DATABASE_RECORD Record;
|
|
|
|
//
|
|
// Locate PI SMM CPU protocol
|
|
//
|
|
Status = gSmst->SmmLocateProtocol (&gEfiSmmCpuProtocolGuid, NULL, (VOID **)&mSmmCpuProtocol);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
//
|
|
// Initialize SW SMI Callback DataBase
|
|
//
|
|
InitializeListHead (&mSwSmiCallbackDataBase);
|
|
|
|
//
|
|
// Insert SwSmi handler to PchSmmCore database
|
|
// There will always be one SwType record in PchSmmCore database
|
|
//
|
|
ZeroMem (&Record, sizeof (DATABASE_RECORD));
|
|
Record.Signature = DATABASE_RECORD_SIGNATURE;
|
|
Record.Callback = PchSwSmiDispatcher;
|
|
Record.ProtocolType = SwType;
|
|
|
|
CopyMem (&Record.SrcDesc, &mSwSourceDesc, sizeof (PCH_SMM_SOURCE_DESC));
|
|
|
|
DispatchHandle = NULL;
|
|
Status = SmmCoreInsertRecord (
|
|
&Record,
|
|
&DispatchHandle
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
}
|