mirror of https://github.com/acidanthera/audk.git
259 lines
7.5 KiB
C
259 lines
7.5 KiB
C
/** @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;
|
|
}
|