/** @file
This module produces the SMM Control2 Protocol
Copyright (c) 2021, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SMM_DATA_PORT 0xB3
#define SMM_CONTROL_PORT 0xB2
typedef struct {
UINT8 GblBitOffset;
UINT8 ApmBitOffset;
UINT32 Address;
} SMM_CONTROL2_REG;
SMM_CONTROL2_REG mSmiCtrlReg;
/**
Invokes SMI activation from either the preboot or runtime environment.
This function generates an SMI.
@param[in] This The EFI_SMM_CONTROL2_PROTOCOL instance.
@param[in,out] CommandPort The value written to the command port.
@param[in,out] DataPort The value written to the data port.
@param[in] Periodic Optional mechanism to engender a periodic stream.
@param[in] ActivationInterval Optional parameter to repeat at this period one
time or, if the Periodic Boolean is set, periodically.
@retval EFI_SUCCESS The SMI has been engendered.
@retval EFI_DEVICE_ERROR The timing is unsupported.
@retval EFI_INVALID_PARAMETER The activation period is unsupported.
@retval EFI_INVALID_PARAMETER The last periodic activation has not been cleared.
@retval EFI_NOT_STARTED The MM base service has not been initialized.
**/
EFI_STATUS
EFIAPI
Activate (
IN CONST EFI_SMM_CONTROL2_PROTOCOL *This,
IN OUT UINT8 *CommandPort OPTIONAL,
IN OUT UINT8 *DataPort OPTIONAL,
IN BOOLEAN Periodic OPTIONAL,
IN EFI_SMM_PERIOD ActivationInterval OPTIONAL
)
{
UINT32 SmiEn;
UINT32 SmiEnableBits;
if (Periodic) {
return EFI_INVALID_PARAMETER;
}
SmiEn = IoRead32 (mSmiCtrlReg.Address);
SmiEnableBits = (1 << mSmiCtrlReg.GblBitOffset) | (1 << mSmiCtrlReg.ApmBitOffset);
if ((SmiEn & SmiEnableBits) != SmiEnableBits) {
//
// Set the "global SMI enable" bit and APM bit
//
IoWrite32 (mSmiCtrlReg.Address, SmiEn | SmiEnableBits);
}
IoWrite8 (SMM_DATA_PORT, DataPort == NULL ? 0 : *DataPort);
IoWrite8 (SMM_CONTROL_PORT, CommandPort == NULL ? 0 : *CommandPort);
return EFI_SUCCESS;
}
/**
Clears an SMI.
@param This Pointer to an instance of EFI_SMM_CONTROL2_PROTOCOL
@param Periodic TRUE to indicate a periodical SMI
@return Return value from SmmClear ()
**/
EFI_STATUS
EFIAPI
Deactivate (
IN CONST EFI_SMM_CONTROL2_PROTOCOL *This,
IN BOOLEAN Periodic
)
{
if (Periodic) {
return EFI_INVALID_PARAMETER;
}
//
// Temporarily do nothing here
//
return EFI_SUCCESS;
}
///
/// SMM COntrol2 Protocol instance
///
EFI_SMM_CONTROL2_PROTOCOL mSmmControl2 = {
Activate,
Deactivate,
0
};
/**
Get specified SMI register based on given register ID
@param[in] SmmRegister SMI related register array from bootloader
@param[in] Id The register ID to get.
@retval NULL The register is not found or the format is not expected.
@return smi register
**/
PLD_GENERIC_REGISTER *
GetSmmCtrlRegById (
IN PLD_SMM_REGISTERS *SmmRegister,
IN UINT32 Id
)
{
UINT32 Index;
PLD_GENERIC_REGISTER *PldReg;
PldReg = NULL;
for (Index = 0; Index < SmmRegister->Count; Index++) {
if (SmmRegister->Registers[Index].Id == Id) {
PldReg = &SmmRegister->Registers[Index];
break;
}
}
if (PldReg == NULL) {
DEBUG ((DEBUG_INFO, "Register %d not found.\n", Id));
return NULL;
}
//
// Checking the register if it is expected.
//
if ((PldReg->Address.AccessSize != EFI_ACPI_3_0_DWORD) ||
(PldReg->Address.Address == 0) ||
(PldReg->Address.RegisterBitWidth != 1) ||
(PldReg->Address.AddressSpaceId != EFI_ACPI_3_0_SYSTEM_IO) ||
(PldReg->Value != 1))
{
DEBUG ((DEBUG_INFO, "Unexpected SMM register.\n"));
DEBUG ((DEBUG_INFO, "AddressSpaceId= 0x%x\n", PldReg->Address.AddressSpaceId));
DEBUG ((DEBUG_INFO, "RegBitWidth = 0x%x\n", PldReg->Address.RegisterBitWidth));
DEBUG ((DEBUG_INFO, "RegBitOffset = 0x%x\n", PldReg->Address.RegisterBitOffset));
DEBUG ((DEBUG_INFO, "AccessSize = 0x%x\n", PldReg->Address.AccessSize));
DEBUG ((DEBUG_INFO, "Address = 0x%lx\n", PldReg->Address.Address));
return NULL;
}
return PldReg;
}
/**
Fixup data pointers so that the services can be called in virtual mode.
@param[in] Event The event registered.
@param[in] Context Event context.
**/
VOID
EFIAPI
SmmControlVirtualAddressChangeEvent (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EfiConvertPointer (0x0, (VOID **)&(mSmmControl2.Trigger));
EfiConvertPointer (0x0, (VOID **)&(mSmmControl2.Clear));
}
/**
This function installs EFI_SMM_CONTROL2_PROTOCOL.
@param ImageHandle Handle for the image of this driver
@param SystemTable Pointer to the EFI System Table
@retval EFI_UNSUPPORTED There's no Intel ICH on this platform
@return The status returned from InstallProtocolInterface().
**/
EFI_STATUS
EFIAPI
SmmControlEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_HOB_GUID_TYPE *GuidHob;
PLD_SMM_REGISTERS *SmmRegister;
PLD_GENERIC_REGISTER *SmiGblEnReg;
PLD_GENERIC_REGISTER *SmiApmEnReg;
EFI_EVENT Event;
GuidHob = GetFirstGuidHob (&gSmmRegisterInfoGuid);
if (GuidHob == NULL) {
return EFI_UNSUPPORTED;
}
SmmRegister = (PLD_SMM_REGISTERS *)(GET_GUID_HOB_DATA (GuidHob));
SmiGblEnReg = GetSmmCtrlRegById (SmmRegister, REGISTER_ID_SMI_GBL_EN);
if (SmiGblEnReg == NULL) {
DEBUG ((DEBUG_ERROR, "SMI global enable reg not found.\n"));
return EFI_NOT_FOUND;
}
mSmiCtrlReg.Address = (UINT32)SmiGblEnReg->Address.Address;
mSmiCtrlReg.GblBitOffset = SmiGblEnReg->Address.RegisterBitOffset;
SmiApmEnReg = GetSmmCtrlRegById (SmmRegister, REGISTER_ID_SMI_APM_EN);
if (SmiApmEnReg == NULL) {
DEBUG ((DEBUG_ERROR, "SMI APM enable reg not found.\n"));
return EFI_NOT_FOUND;
}
if (SmiApmEnReg->Address.Address != mSmiCtrlReg.Address) {
DEBUG ((DEBUG_ERROR, "SMI APM EN and SMI GBL EN are expected to have same register base\n"));
DEBUG ((DEBUG_ERROR, "APM:0x%x, GBL:0x%x\n", SmiApmEnReg->Address.Address, mSmiCtrlReg.Address));
return EFI_UNSUPPORTED;
}
mSmiCtrlReg.ApmBitOffset = SmiApmEnReg->Address.RegisterBitOffset;
//
// Install our protocol interfaces on the device's handle
//
Status = gBS->InstallMultipleProtocolInterfaces (
&ImageHandle,
&gEfiSmmControl2ProtocolGuid,
&mSmmControl2,
NULL
);
ASSERT_EFI_ERROR (Status);
Status = gBS->CreateEventEx (
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
SmmControlVirtualAddressChangeEvent,
NULL,
&gEfiEventVirtualAddressChangeGuid,
&Event
);
return Status;
}