123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455 |
- /** @file
- SMM SwDispatch2 Protocol.
- Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
- SPDX-License-Identifier: BSD-2-Clause-Patent
- **/
- #include "PchSmiDispatchSmm.h"
- typedef struct {
- UINT8 EosBitOffset;
- UINT8 ApmBitOffset;
- UINT32 SmiEosAddr;
- UINT32 SmiApmStsAddr;
- } SMM_PCH_REGISTER;
- SMM_PCH_REGISTER mSmiPchReg;
- EFI_SMM_CPU_PROTOCOL *mSmmCpuProtocol;
- LIST_ENTRY mSmmSwDispatch2Queue = INITIALIZE_LIST_HEAD_VARIABLE (mSmmSwDispatch2Queue);
- /**
- Find SmmSwDispatch2Context by SwSmiInputValue.
- @param[in] SwSmiInputValue The value to indentify the SmmSwDispatch2 context
- @return Pointer to EFI_SMM_SW_DISPATCH2_CONTEXT context
- **/
- EFI_SMM_SW_DISPATCH2_CONTEXT *
- FindContextBySwSmiInputValue (
- IN UINTN SwSmiInputValue
- )
- {
- LIST_ENTRY *Node;
- EFI_SMM_SW_DISPATCH2_CONTEXT *Dispatch2Context;
- Node = mSmmSwDispatch2Queue.ForwardLink;
- for ( ; Node != &mSmmSwDispatch2Queue; Node = Node->ForwardLink) {
- Dispatch2Context = BASE_CR (Node, EFI_SMM_SW_DISPATCH2_CONTEXT, Link);
- if (Dispatch2Context->SwSmiInputValue == SwSmiInputValue) {
- return Dispatch2Context;
- }
- }
- return NULL;
- }
- /**
- Find SmmSwDispatch2Context by DispatchHandle.
- @param DispatchHandle The handle to indentify the SmmSwDispatch2 context
- @return Pointer to EFI_SMM_SW_DISPATCH2_CONTEXT context
- **/
- EFI_SMM_SW_DISPATCH2_CONTEXT *
- FindContextByDispatchHandle (
- IN EFI_HANDLE DispatchHandle
- )
- {
- LIST_ENTRY *Node;
- EFI_SMM_SW_DISPATCH2_CONTEXT *Dispatch2Context;
- Node = mSmmSwDispatch2Queue.ForwardLink;
- for ( ; Node != &mSmmSwDispatch2Queue; Node = Node->ForwardLink) {
- Dispatch2Context = BASE_CR (Node, EFI_SMM_SW_DISPATCH2_CONTEXT, Link);
- if (Dispatch2Context->DispatchHandle == DispatchHandle) {
- return Dispatch2Context;
- }
- }
- return NULL;
- }
- /**
- Dispatch registered SMM handlers
- @param DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
- @param RegisterContext Points to an optional handler context which was specified when the handler was registered.
- @param CommBuffer A pointer to a collection of data in memory that will
- be conveyed from a non-SMM environment into an SMM environment.
- @param CommBufferSize The size of the CommBuffer.
- @return Status Code
- **/
- EFI_STATUS
- SmmSwDispatcher (
- IN EFI_HANDLE DispatchHandle,
- IN CONST VOID *RegisterContext,
- IN OUT VOID *CommBuffer,
- IN OUT UINTN *CommBufferSize
- )
- {
- EFI_STATUS Status;
- EFI_SMM_SW_CONTEXT SwContext;
- UINTN Index;
- EFI_SMM_SW_DISPATCH2_CONTEXT *Context;
- EFI_SMM_HANDLER_ENTRY_POINT2 DispatchFunction;
- EFI_SMM_SW_REGISTER_CONTEXT DispatchContext;
- UINTN Size;
- EFI_SMM_SAVE_STATE_IO_INFO IoInfo;
- //
- // Construct new context
- //
- SwContext.SwSmiCpuIndex = 0;
- SwContext.CommandPort = IoRead8 (SMM_CONTROL_PORT);
- SwContext.DataPort = IoRead8 (SMM_DATA_PORT);
- //
- // Try to find which CPU trigger SWSMI
- //
- for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {
- Status = mSmmCpuProtocol->ReadSaveState (
- mSmmCpuProtocol,
- sizeof (IoInfo),
- EFI_SMM_SAVE_STATE_REGISTER_IO,
- Index,
- &IoInfo
- );
- if (EFI_ERROR (Status)) {
- continue;
- }
- if (IoInfo.IoPort == SMM_CONTROL_PORT) {
- //
- // Great! Find it.
- //
- SwContext.SwSmiCpuIndex = Index;
- DEBUG ((DEBUG_VERBOSE, "CPU index = 0x%x/0x%x\n", Index, gSmst->NumberOfCpus));
- break;
- }
- }
- if (SwContext.CommandPort == 0) {
- DEBUG ((DEBUG_VERBOSE, "NOT SW SMI\n"));
- Status = EFI_SUCCESS;
- goto End;
- }
- //
- // Search context
- //
- Context = FindContextBySwSmiInputValue (SwContext.CommandPort);
- if (Context == NULL) {
- DEBUG ((DEBUG_INFO, "No handler for SMI value 0x%x\n", SwContext.CommandPort));
- Status = EFI_SUCCESS;
- goto End;
- }
- DEBUG ((DEBUG_VERBOSE, "Prepare to call handler for 0x%x\n", SwContext.CommandPort));
- //
- // Dispatch
- //
- DispatchContext.SwSmiInputValue = SwContext.CommandPort;
- Size = sizeof (SwContext);
- DispatchFunction = (EFI_SMM_HANDLER_ENTRY_POINT2)Context->DispatchFunction;
- Status = DispatchFunction (DispatchHandle, &DispatchContext, &SwContext, &Size);
- End:
- //
- // Clear SMI APM status
- //
- IoOr32 (mSmiPchReg.SmiApmStsAddr, 1 << mSmiPchReg.ApmBitOffset);
- //
- // Set EOS bit
- //
- IoOr32 (mSmiPchReg.SmiEosAddr, 1 << mSmiPchReg.EosBitOffset);
- return Status;
- }
- /**
- Check the SwSmiInputValue is already used
- @param[in] SwSmiInputValue To indentify the SmmSwDispatch2 context
- @retval EFI_SUCCESS SwSmiInputValue could be used.
- @retval EFI_INVALID_PARAMETER SwSmiInputValue is already be used.
- **/
- EFI_STATUS
- SmiInputValueCheck (
- IN UINTN SwSmiInputValue
- )
- {
- LIST_ENTRY *Node;
- EFI_SMM_SW_DISPATCH2_CONTEXT *Dispatch2Context;
- Node = mSmmSwDispatch2Queue.ForwardLink;
- for ( ; Node != &mSmmSwDispatch2Queue; Node = Node->ForwardLink) {
- Dispatch2Context = BASE_CR (Node, EFI_SMM_SW_DISPATCH2_CONTEXT, Link);
- if (Dispatch2Context->SwSmiInputValue == SwSmiInputValue) {
- return EFI_INVALID_PARAMETER;
- }
- }
- 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 RegContext->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] RegContext 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 valid range.
- @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
- SmmSwDispatch2Register (
- IN CONST EFI_SMM_SW_DISPATCH2_PROTOCOL *This,
- IN EFI_SMM_HANDLER_ENTRY_POINT2 DispatchFunction,
- IN OUT EFI_SMM_SW_REGISTER_CONTEXT *RegContext,
- OUT EFI_HANDLE *DispatchHandle
- )
- {
- EFI_STATUS Status;
- UINTN Index;
- EFI_SMM_SW_DISPATCH2_CONTEXT *Context;
- if (RegContext->SwSmiInputValue == (UINTN)-1) {
- //
- // If SwSmiInputValue is set to (UINTN) -1 then a unique value
- // will be assigned and returned in the structure.
- //
- Status = EFI_NOT_FOUND;
- for (Index = 1; Index < MAXIMUM_SWI_VALUE; Index++) {
- Status = SmiInputValueCheck (Index);
- if (!EFI_ERROR (Status)) {
- RegContext->SwSmiInputValue = Index;
- break;
- }
- }
- if (RegContext->SwSmiInputValue == (UINTN)-1) {
- return EFI_OUT_OF_RESOURCES;
- }
- }
- if ((RegContext->SwSmiInputValue > MAXIMUM_SWI_VALUE) || (RegContext->SwSmiInputValue == 0)) {
- DEBUG ((DEBUG_ERROR, "ERROR: SMI value range (1 ~ 0x%x)\n", MAXIMUM_SWI_VALUE));
- return EFI_INVALID_PARAMETER;
- }
- //
- // Register
- //
- Status = gSmst->SmmAllocatePool (EfiRuntimeServicesData, sizeof (*Context), (VOID **)&Context);
- ASSERT_EFI_ERROR (Status);
- if (EFI_ERROR (Status)) {
- return EFI_OUT_OF_RESOURCES;
- }
- *DispatchHandle = (EFI_HANDLE)Context;
- Context->Signature = SMI_SW_HANDLER_SIGNATURE;
- Context->SwSmiInputValue = RegContext->SwSmiInputValue;
- Context->DispatchFunction = (UINTN)DispatchFunction;
- Context->DispatchHandle = *DispatchHandle;
- InsertTailList (&mSmmSwDispatch2Queue, &Context->Link);
- return Status;
- }
- /**
- 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
- SmmSwDispatch2UnRegister (
- IN CONST EFI_SMM_SW_DISPATCH2_PROTOCOL *This,
- IN EFI_HANDLE DispatchHandle
- )
- {
- EFI_SMM_SW_DISPATCH2_CONTEXT *Context;
- //
- // Unregister
- //
- Context = FindContextByDispatchHandle (DispatchHandle);
- ASSERT (Context != NULL);
- if (Context != NULL) {
- RemoveEntryList (&Context->Link);
- gSmst->SmmFreePool (Context);
- }
- return EFI_SUCCESS;
- }
- EFI_SMM_SW_DISPATCH2_PROTOCOL gSmmSwDispatch2 = {
- SmmSwDispatch2Register,
- SmmSwDispatch2UnRegister,
- MAXIMUM_SWI_VALUE
- };
- /**
- 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;
- }
- /**
- Entry Point for this driver.
- @param[in] ImageHandle Image handle of this driver.
- @param[in] SystemTable A Pointer to the EFI System Table.
- @retval EFI_SUCCESS The entry point is executed successfully.
- @retval other Some error occurred when executing this entry point.
- **/
- EFI_STATUS
- EFIAPI
- PchSmiDispatchEntryPoint (
- IN EFI_HANDLE ImageHandle,
- IN EFI_SYSTEM_TABLE *SystemTable
- )
- {
- EFI_STATUS Status;
- EFI_HANDLE DispatchHandle;
- EFI_HOB_GUID_TYPE *GuidHob;
- PLD_SMM_REGISTERS *SmmRegister;
- PLD_GENERIC_REGISTER *SmiEosReg;
- PLD_GENERIC_REGISTER *SmiApmStsReg;
- GuidHob = GetFirstGuidHob (&gSmmRegisterInfoGuid);
- if (GuidHob == NULL) {
- return EFI_UNSUPPORTED;
- }
- SmmRegister = (PLD_SMM_REGISTERS *)GET_GUID_HOB_DATA (GuidHob);
- SmiEosReg = GetSmmCtrlRegById (SmmRegister, REGISTER_ID_SMI_EOS);
- if (SmiEosReg == NULL) {
- DEBUG ((DEBUG_ERROR, "SMI EOS reg not found.\n"));
- return EFI_NOT_FOUND;
- }
- mSmiPchReg.SmiEosAddr = (UINT32)SmiEosReg->Address.Address;
- mSmiPchReg.EosBitOffset = SmiEosReg->Address.RegisterBitOffset;
- SmiApmStsReg = GetSmmCtrlRegById (SmmRegister, REGISTER_ID_SMI_APM_STS);
- if (SmiApmStsReg == NULL) {
- DEBUG ((DEBUG_ERROR, "SMI APM status reg not found.\n"));
- return EFI_NOT_FOUND;
- }
- mSmiPchReg.SmiApmStsAddr = (UINT32)SmiApmStsReg->Address.Address;
- mSmiPchReg.ApmBitOffset = SmiApmStsReg->Address.RegisterBitOffset;
- //
- // Locate PI SMM CPU protocol
- //
- Status = gSmst->SmmLocateProtocol (&gEfiSmmCpuProtocolGuid, NULL, (VOID **)&mSmmCpuProtocol);
- ASSERT_EFI_ERROR (Status);
- //
- // Register a SMM handler to handle subsequent SW SMIs.
- //
- Status = gSmst->SmiHandlerRegister ((EFI_MM_HANDLER_ENTRY_POINT)SmmSwDispatcher, NULL, &DispatchHandle);
- ASSERT_EFI_ERROR (Status);
- //
- // Publish PI SMM SwDispatch2 Protocol
- //
- ImageHandle = NULL;
- Status = gSmst->SmmInstallProtocolInterface (
- &ImageHandle,
- &gEfiSmmSwDispatch2ProtocolGuid,
- EFI_NATIVE_INTERFACE,
- &gSmmSwDispatch2
- );
- ASSERT_EFI_ERROR (Status);
- return Status;
- }
|