/** @file | |
This module produces the SMM Control2 Protocol | |
Copyright (c) 2021, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include <PiDxe.h> | |
#include <Protocol/SmmControl2.h> | |
#include <Library/DebugLib.h> | |
#include <Library/UefiBootServicesTableLib.h> | |
#include <Library/IoLib.h> | |
#include <Library/HobLib.h> | |
#include <Library/UefiRuntimeLib.h> | |
#include <Library/BaseMemoryLib.h> | |
#include <Guid/SmmRegisterInfoGuid.h> | |
#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; | |
} |