/** @file Locate handle functions Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "PiSmmCore.h" // // ProtocolRequest - Last LocateHandle request ID // UINTN mEfiLocateHandleRequest = 0; // // Internal prototypes // typedef struct { EFI_GUID *Protocol; VOID *SearchKey; LIST_ENTRY *Position; PROTOCOL_ENTRY *ProtEntry; } LOCATE_POSITION; typedef IHANDLE * (*CORE_GET_NEXT) ( IN OUT LOCATE_POSITION *Position, OUT VOID **Interface ); /** Routine to get the next Handle, when you are searching for all handles. @param Position Information about which Handle to search for. @param Interface Return the interface structure for the matching protocol. @return An pointer to IHANDLE if the next Position is not the end of the list. Otherwise,NULL is returned. **/ IHANDLE * SmmGetNextLocateAllHandles ( IN OUT LOCATE_POSITION *Position, OUT VOID **Interface ) { IHANDLE *Handle; // // Next handle // Position->Position = Position->Position->ForwardLink; // // If not at the end of the list, get the handle // Handle = NULL; *Interface = NULL; if (Position->Position != &gHandleList) { Handle = CR (Position->Position, IHANDLE, AllHandles, EFI_HANDLE_SIGNATURE); } return Handle; } /** Routine to get the next Handle, when you are searching for register protocol notifies. @param Position Information about which Handle to search for. @param Interface Return the interface structure for the matching protocol. @return An pointer to IHANDLE if the next Position is not the end of the list. Otherwise,NULL is returned. **/ IHANDLE * SmmGetNextLocateByRegisterNotify ( IN OUT LOCATE_POSITION *Position, OUT VOID **Interface ) { IHANDLE *Handle; PROTOCOL_NOTIFY *ProtNotify; PROTOCOL_INTERFACE *Prot; LIST_ENTRY *Link; Handle = NULL; *Interface = NULL; ProtNotify = Position->SearchKey; // // If this is the first request, get the next handle // if (ProtNotify != NULL) { ASSERT (ProtNotify->Signature == PROTOCOL_NOTIFY_SIGNATURE); Position->SearchKey = NULL; // // If not at the end of the list, get the next handle // Link = ProtNotify->Position->ForwardLink; if (Link != &ProtNotify->Protocol->Protocols) { Prot = CR (Link, PROTOCOL_INTERFACE, ByProtocol, PROTOCOL_INTERFACE_SIGNATURE); Handle = Prot->Handle; *Interface = Prot->Interface; } } return Handle; } /** Routine to get the next Handle, when you are searching for a given protocol. @param Position Information about which Handle to search for. @param Interface Return the interface structure for the matching protocol. @return An pointer to IHANDLE if the next Position is not the end of the list. Otherwise,NULL is returned. **/ IHANDLE * SmmGetNextLocateByProtocol ( IN OUT LOCATE_POSITION *Position, OUT VOID **Interface ) { IHANDLE *Handle; LIST_ENTRY *Link; PROTOCOL_INTERFACE *Prot; Handle = NULL; *Interface = NULL; for ( ; ;) { // // Next entry // Link = Position->Position->ForwardLink; Position->Position = Link; // // If not at the end, return the handle // if (Link == &Position->ProtEntry->Protocols) { Handle = NULL; break; } // // Get the handle // Prot = CR (Link, PROTOCOL_INTERFACE, ByProtocol, PROTOCOL_INTERFACE_SIGNATURE); Handle = Prot->Handle; *Interface = Prot->Interface; // // If this handle has not been returned this request, then // return it now // if (Handle->LocateRequest != mEfiLocateHandleRequest) { Handle->LocateRequest = mEfiLocateHandleRequest; break; } } return Handle; } /** Return the first Protocol Interface that matches the Protocol GUID. If Registration is pasased in return a Protocol Instance that was just add to the system. If Registration is NULL return the first Protocol Interface you find. @param Protocol The protocol to search for @param Registration Optional Registration Key returned from RegisterProtocolNotify() @param Interface Return the Protocol interface (instance). @retval EFI_SUCCESS If a valid Interface is returned @retval EFI_INVALID_PARAMETER Invalid parameter @retval EFI_NOT_FOUND Protocol interface not found **/ EFI_STATUS EFIAPI SmmLocateProtocol ( IN EFI_GUID *Protocol, IN VOID *Registration OPTIONAL, OUT VOID **Interface ) { EFI_STATUS Status; LOCATE_POSITION Position; PROTOCOL_NOTIFY *ProtNotify; IHANDLE *Handle; if ((Interface == NULL) || (Protocol == NULL)) { return EFI_INVALID_PARAMETER; } *Interface = NULL; Status = EFI_SUCCESS; // // Set initial position // Position.Protocol = Protocol; Position.SearchKey = Registration; Position.Position = &gHandleList; mEfiLocateHandleRequest += 1; if (Registration == NULL) { // // Look up the protocol entry and set the head pointer // Position.ProtEntry = SmmFindProtocolEntry (Protocol, FALSE); if (Position.ProtEntry == NULL) { return EFI_NOT_FOUND; } Position.Position = &Position.ProtEntry->Protocols; Handle = SmmGetNextLocateByProtocol (&Position, Interface); } else { Handle = SmmGetNextLocateByRegisterNotify (&Position, Interface); } if (Handle == NULL) { Status = EFI_NOT_FOUND; } else if (Registration != NULL) { // // If this is a search by register notify and a handle was // returned, update the register notification position // ProtNotify = Registration; ProtNotify->Position = ProtNotify->Position->ForwardLink; } return Status; } /** Locates the requested handle(s) and returns them in Buffer. @param SearchType The type of search to perform to locate the handles @param Protocol The protocol to search for @param SearchKey Dependant on SearchType @param BufferSize On input the size of Buffer. On output the size of data returned. @param Buffer The buffer to return the results in @retval EFI_BUFFER_TOO_SMALL Buffer too small, required buffer size is returned in BufferSize. @retval EFI_INVALID_PARAMETER Invalid parameter @retval EFI_SUCCESS Successfully found the requested handle(s) and returns them in Buffer. **/ EFI_STATUS EFIAPI SmmLocateHandle ( IN EFI_LOCATE_SEARCH_TYPE SearchType, IN EFI_GUID *Protocol OPTIONAL, IN VOID *SearchKey OPTIONAL, IN OUT UINTN *BufferSize, OUT EFI_HANDLE *Buffer ) { EFI_STATUS Status; LOCATE_POSITION Position; PROTOCOL_NOTIFY *ProtNotify; CORE_GET_NEXT GetNext; UINTN ResultSize; IHANDLE *Handle; IHANDLE **ResultBuffer; VOID *Interface; if (BufferSize == NULL) { return EFI_INVALID_PARAMETER; } if ((*BufferSize > 0) && (Buffer == NULL)) { return EFI_INVALID_PARAMETER; } GetNext = NULL; // // Set initial position // Position.Protocol = Protocol; Position.SearchKey = SearchKey; Position.Position = &gHandleList; ResultSize = 0; ResultBuffer = (IHANDLE **)Buffer; Status = EFI_SUCCESS; // // Get the search function based on type // switch (SearchType) { case AllHandles: GetNext = SmmGetNextLocateAllHandles; break; case ByRegisterNotify: GetNext = SmmGetNextLocateByRegisterNotify; // // Must have SearchKey for locate ByRegisterNotify // if (SearchKey == NULL) { Status = EFI_INVALID_PARAMETER; } break; case ByProtocol: GetNext = SmmGetNextLocateByProtocol; if (Protocol == NULL) { Status = EFI_INVALID_PARAMETER; break; } // // Look up the protocol entry and set the head pointer // Position.ProtEntry = SmmFindProtocolEntry (Protocol, FALSE); if (Position.ProtEntry == NULL) { Status = EFI_NOT_FOUND; break; } Position.Position = &Position.ProtEntry->Protocols; break; default: Status = EFI_INVALID_PARAMETER; break; } if (EFI_ERROR (Status)) { return Status; } // // Enumerate out the matching handles // mEfiLocateHandleRequest += 1; for ( ; ;) { // // Get the next handle. If no more handles, stop // Handle = GetNext (&Position, &Interface); if (NULL == Handle) { break; } // // Increase the resulting buffer size, and if this handle // fits return it // ResultSize += sizeof (Handle); if (ResultSize <= *BufferSize) { *ResultBuffer = Handle; ResultBuffer += 1; } } // // If the result is a zero length buffer, then there were no // matching handles // if (ResultSize == 0) { Status = EFI_NOT_FOUND; } else { // // Return the resulting buffer size. If it's larger than what // was passed, then set the error code // if (ResultSize > *BufferSize) { Status = EFI_BUFFER_TOO_SMALL; } *BufferSize = ResultSize; if ((SearchType == ByRegisterNotify) && !EFI_ERROR (Status)) { ASSERT (SearchKey != NULL); // // If this is a search by register notify and a handle was // returned, update the register notification position // ProtNotify = SearchKey; ProtNotify->Position = ProtNotify->Position->ForwardLink; } } return Status; } /** Function returns an array of handles that support the requested protocol in a buffer allocated from pool. This is a version of SmmLocateHandle() that allocates a buffer for the caller. @param SearchType Specifies which handle(s) are to be returned. @param Protocol Provides the protocol to search by. This parameter is only valid for SearchType ByProtocol. @param SearchKey Supplies the search key depending on the SearchType. @param NumberHandles The number of handles returned in Buffer. @param Buffer A pointer to the buffer to return the requested array of handles that support Protocol. @retval EFI_SUCCESS The result array of handles was returned. @retval EFI_NOT_FOUND No handles match the search. @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the matching results. @retval EFI_INVALID_PARAMETER One or more parameters are not valid. **/ EFI_STATUS EFIAPI SmmLocateHandleBuffer ( IN EFI_LOCATE_SEARCH_TYPE SearchType, IN EFI_GUID *Protocol OPTIONAL, IN VOID *SearchKey OPTIONAL, IN OUT UINTN *NumberHandles, OUT EFI_HANDLE **Buffer ) { EFI_STATUS Status; UINTN BufferSize; if (NumberHandles == NULL) { return EFI_INVALID_PARAMETER; } if (Buffer == NULL) { return EFI_INVALID_PARAMETER; } BufferSize = 0; *NumberHandles = 0; *Buffer = NULL; Status = SmmLocateHandle ( SearchType, Protocol, SearchKey, &BufferSize, *Buffer ); // // LocateHandleBuffer() returns incorrect status code if SearchType is // invalid. // // Add code to correctly handle expected errors from SmmLocateHandle(). // if (EFI_ERROR (Status) && (Status != EFI_BUFFER_TOO_SMALL)) { if (Status != EFI_INVALID_PARAMETER) { Status = EFI_NOT_FOUND; } return Status; } *Buffer = AllocatePool (BufferSize); if (*Buffer == NULL) { return EFI_OUT_OF_RESOURCES; } Status = SmmLocateHandle ( SearchType, Protocol, SearchKey, &BufferSize, *Buffer ); *NumberHandles = BufferSize / sizeof (EFI_HANDLE); if (EFI_ERROR (Status)) { *NumberHandles = 0; } return Status; }