/** @file SMM handle & protocol handling. Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "PiSmmCore.h" // // mProtocolDatabase - A list of all protocols in the system. (simple list for now) // gHandleList - A list of all the handles in the system // LIST_ENTRY mProtocolDatabase = INITIALIZE_LIST_HEAD_VARIABLE (mProtocolDatabase); LIST_ENTRY gHandleList = INITIALIZE_LIST_HEAD_VARIABLE (gHandleList); /** Check whether a handle is a valid EFI_HANDLE @param UserHandle The handle to check @retval EFI_INVALID_PARAMETER The handle is NULL or not a valid EFI_HANDLE. @retval EFI_SUCCESS The handle is valid EFI_HANDLE. **/ EFI_STATUS SmmValidateHandle ( IN EFI_HANDLE UserHandle ) { IHANDLE *Handle; Handle = (IHANDLE *)UserHandle; if (Handle == NULL) { return EFI_INVALID_PARAMETER; } if (Handle->Signature != EFI_HANDLE_SIGNATURE) { return EFI_INVALID_PARAMETER; } return EFI_SUCCESS; } /** Finds the protocol entry for the requested protocol. @param Protocol The ID of the protocol @param Create Create a new entry if not found @return Protocol entry **/ PROTOCOL_ENTRY * SmmFindProtocolEntry ( IN EFI_GUID *Protocol, IN BOOLEAN Create ) { LIST_ENTRY *Link; PROTOCOL_ENTRY *Item; PROTOCOL_ENTRY *ProtEntry; // // Search the database for the matching GUID // ProtEntry = NULL; for (Link = mProtocolDatabase.ForwardLink; Link != &mProtocolDatabase; Link = Link->ForwardLink) { Item = CR(Link, PROTOCOL_ENTRY, AllEntries, PROTOCOL_ENTRY_SIGNATURE); if (CompareGuid (&Item->ProtocolID, Protocol)) { // // This is the protocol entry // ProtEntry = Item; break; } } // // If the protocol entry was not found and Create is TRUE, then // allocate a new entry // if ((ProtEntry == NULL) && Create) { ProtEntry = AllocatePool (sizeof(PROTOCOL_ENTRY)); if (ProtEntry != NULL) { // // Initialize new protocol entry structure // ProtEntry->Signature = PROTOCOL_ENTRY_SIGNATURE; CopyGuid ((VOID *)&ProtEntry->ProtocolID, Protocol); InitializeListHead (&ProtEntry->Protocols); InitializeListHead (&ProtEntry->Notify); // // Add it to protocol database // InsertTailList (&mProtocolDatabase, &ProtEntry->AllEntries); } } return ProtEntry; } /** Finds the protocol instance for the requested handle and protocol. Note: This function doesn't do parameters checking, it's caller's responsibility to pass in valid parameters. @param Handle The handle to search the protocol on @param Protocol GUID of the protocol @param Interface The interface for the protocol being searched @return Protocol instance (NULL: Not found) **/ PROTOCOL_INTERFACE * SmmFindProtocolInterface ( IN IHANDLE *Handle, IN EFI_GUID *Protocol, IN VOID *Interface ) { PROTOCOL_INTERFACE *Prot; PROTOCOL_ENTRY *ProtEntry; LIST_ENTRY *Link; Prot = NULL; // // Lookup the protocol entry for this protocol ID // ProtEntry = SmmFindProtocolEntry (Protocol, FALSE); if (ProtEntry != NULL) { // // Look at each protocol interface for any matches // for (Link = Handle->Protocols.ForwardLink; Link != &Handle->Protocols; Link=Link->ForwardLink) { // // If this protocol interface matches, remove it // Prot = CR(Link, PROTOCOL_INTERFACE, Link, PROTOCOL_INTERFACE_SIGNATURE); if (Prot->Interface == Interface && Prot->Protocol == ProtEntry) { break; } Prot = NULL; } } return Prot; } /** Wrapper function to SmmInstallProtocolInterfaceNotify. This is the public API which Calls the private one which contains a BOOLEAN parameter for notifications @param UserHandle The handle to install the protocol handler on, or NULL if a new handle is to be allocated @param Protocol The protocol to add to the handle @param InterfaceType Indicates whether Interface is supplied in native form. @param Interface The interface for the protocol being added @return Status code **/ EFI_STATUS EFIAPI SmmInstallProtocolInterface ( IN OUT EFI_HANDLE *UserHandle, IN EFI_GUID *Protocol, IN EFI_INTERFACE_TYPE InterfaceType, IN VOID *Interface ) { return SmmInstallProtocolInterfaceNotify ( UserHandle, Protocol, InterfaceType, Interface, TRUE ); } /** Installs a protocol interface into the boot services environment. @param UserHandle The handle to install the protocol handler on, or NULL if a new handle is to be allocated @param Protocol The protocol to add to the handle @param InterfaceType Indicates whether Interface is supplied in native form. @param Interface The interface for the protocol being added @param Notify indicates whether notify the notification list for this protocol @retval EFI_INVALID_PARAMETER Invalid parameter @retval EFI_OUT_OF_RESOURCES No enough buffer to allocate @retval EFI_SUCCESS Protocol interface successfully installed **/ EFI_STATUS SmmInstallProtocolInterfaceNotify ( IN OUT EFI_HANDLE *UserHandle, IN EFI_GUID *Protocol, IN EFI_INTERFACE_TYPE InterfaceType, IN VOID *Interface, IN BOOLEAN Notify ) { PROTOCOL_INTERFACE *Prot; PROTOCOL_ENTRY *ProtEntry; IHANDLE *Handle; EFI_STATUS Status; VOID *ExistingInterface; // // returns EFI_INVALID_PARAMETER if InterfaceType is invalid. // Also added check for invalid UserHandle and Protocol pointers. // if (UserHandle == NULL || Protocol == NULL) { return EFI_INVALID_PARAMETER; } if (InterfaceType != EFI_NATIVE_INTERFACE) { return EFI_INVALID_PARAMETER; } // // Print debug message // DEBUG((DEBUG_LOAD | DEBUG_INFO, "SmmInstallProtocolInterface: %g %p\n", Protocol, Interface)); Status = EFI_OUT_OF_RESOURCES; Prot = NULL; Handle = NULL; if (*UserHandle != NULL) { Status = SmmHandleProtocol (*UserHandle, Protocol, (VOID **)&ExistingInterface); if (!EFI_ERROR(Status)) { return EFI_INVALID_PARAMETER; } } // // Lookup the Protocol Entry for the requested protocol // ProtEntry = SmmFindProtocolEntry (Protocol, TRUE); if (ProtEntry == NULL) { goto Done; } // // Allocate a new protocol interface structure // Prot = AllocateZeroPool (sizeof(PROTOCOL_INTERFACE)); if (Prot == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Done; } // // If caller didn't supply a handle, allocate a new one // Handle = (IHANDLE *)*UserHandle; if (Handle == NULL) { Handle = AllocateZeroPool (sizeof(IHANDLE)); if (Handle == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Done; } // // Initialize new handler structure // Handle->Signature = EFI_HANDLE_SIGNATURE; InitializeListHead (&Handle->Protocols); // // Add this handle to the list global list of all handles // in the system // InsertTailList (&gHandleList, &Handle->AllHandles); } else { Status = SmmValidateHandle (Handle); if (EFI_ERROR(Status)) { DEBUG((DEBUG_ERROR, "SmmInstallProtocolInterface: input handle at 0x%x is invalid\n", Handle)); goto Done; } } // // Each interface that is added must be unique // ASSERT (SmmFindProtocolInterface (Handle, Protocol, Interface) == NULL); // // Initialize the protocol interface structure // Prot->Signature = PROTOCOL_INTERFACE_SIGNATURE; Prot->Handle = Handle; Prot->Protocol = ProtEntry; Prot->Interface = Interface; // // Add this protocol interface to the head of the supported // protocol list for this handle // InsertHeadList (&Handle->Protocols, &Prot->Link); // // Add this protocol interface to the tail of the // protocol entry // InsertTailList (&ProtEntry->Protocols, &Prot->ByProtocol); // // Notify the notification list for this protocol // if (Notify) { SmmNotifyProtocol (Prot); } Status = EFI_SUCCESS; Done: if (!EFI_ERROR(Status)) { // // Return the new handle back to the caller // *UserHandle = Handle; } else { // // There was an error, clean up // if (Prot != NULL) { FreePool(Prot); } DEBUG((DEBUG_ERROR, "SmmInstallProtocolInterface: %g %p failed with %r\n", Protocol, Interface, Status)); } return Status; } /** Uninstalls all instances of a protocol:interfacer from a handle. If the last protocol interface is remove from the handle, the handle is freed. @param UserHandle The handle to remove the protocol handler from @param Protocol The protocol, of protocol:interface, to remove @param Interface The interface, of protocol:interface, to remove @retval EFI_INVALID_PARAMETER Protocol is NULL. @retval EFI_SUCCESS Protocol interface successfully uninstalled. **/ EFI_STATUS EFIAPI SmmUninstallProtocolInterface ( IN EFI_HANDLE UserHandle, IN EFI_GUID *Protocol, IN VOID *Interface ) { EFI_STATUS Status; IHANDLE *Handle; PROTOCOL_INTERFACE *Prot; // // Check that Protocol is valid // if (Protocol == NULL) { return EFI_INVALID_PARAMETER; } // // Check that UserHandle is a valid handle // Status = SmmValidateHandle (UserHandle); if (EFI_ERROR(Status)) { return Status; } // // Check that Protocol exists on UserHandle, and Interface matches the interface in the database // Prot = SmmFindProtocolInterface (UserHandle, Protocol, Interface); if (Prot == NULL) { return EFI_NOT_FOUND; } // // Remove the protocol interface from the protocol // Status = EFI_NOT_FOUND; Handle = (IHANDLE *)UserHandle; Prot = SmmRemoveInterfaceFromProtocol (Handle, Protocol, Interface); if (Prot != NULL) { // // Remove the protocol interface from the handle // RemoveEntryList (&Prot->Link); // // Free the memory // Prot->Signature = 0; FreePool(Prot); Status = EFI_SUCCESS; } // // If there are no more handlers for the handle, free the handle // if (IsListEmpty (&Handle->Protocols)) { Handle->Signature = 0; RemoveEntryList (&Handle->AllHandles); FreePool(Handle); } return Status; } /** Locate a certain GUID protocol interface in a Handle's protocols. @param UserHandle The handle to obtain the protocol interface on @param Protocol The GUID of the protocol @return The requested protocol interface for the handle **/ PROTOCOL_INTERFACE * SmmGetProtocolInterface ( IN EFI_HANDLE UserHandle, IN EFI_GUID *Protocol ) { EFI_STATUS Status; PROTOCOL_ENTRY *ProtEntry; PROTOCOL_INTERFACE *Prot; IHANDLE *Handle; LIST_ENTRY *Link; Status = SmmValidateHandle (UserHandle); if (EFI_ERROR(Status)) { return NULL; } Handle = (IHANDLE *)UserHandle; // // Look at each protocol interface for a match // for (Link = Handle->Protocols.ForwardLink; Link != &Handle->Protocols; Link = Link->ForwardLink) { Prot = CR(Link, PROTOCOL_INTERFACE, Link, PROTOCOL_INTERFACE_SIGNATURE); ProtEntry = Prot->Protocol; if (CompareGuid (&ProtEntry->ProtocolID, Protocol)) { return Prot; } } return NULL; } /** Queries a handle to determine if it supports a specified protocol. @param UserHandle The handle being queried. @param Protocol The published unique identifier of the protocol. @param Interface Supplies the address where a pointer to the corresponding Protocol Interface is returned. @retval EFI_SUCCESS The interface information for the specified protocol was returned. @retval EFI_UNSUPPORTED The device does not support the specified protocol. @retval EFI_INVALID_PARAMETER Handle is not a valid EFI_HANDLE.. @retval EFI_INVALID_PARAMETER Protocol is NULL. @retval EFI_INVALID_PARAMETER Interface is NULL. **/ EFI_STATUS EFIAPI SmmHandleProtocol ( IN EFI_HANDLE UserHandle, IN EFI_GUID *Protocol, OUT VOID **Interface ) { EFI_STATUS Status; PROTOCOL_INTERFACE *Prot; // // Check for invalid Protocol // if (Protocol == NULL) { return EFI_INVALID_PARAMETER; } // // Check for invalid Interface // if (Interface == NULL) { return EFI_INVALID_PARAMETER; } else { *Interface = NULL; } // // Check for invalid UserHandle // Status = SmmValidateHandle (UserHandle); if (EFI_ERROR(Status)) { return Status; } // // Look at each protocol interface for a match // Prot = SmmGetProtocolInterface (UserHandle, Protocol); if (Prot == NULL) { return EFI_UNSUPPORTED; } // // This is the protocol interface entry for this protocol // *Interface = Prot->Interface; return EFI_SUCCESS; }