/** @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; }