/** @file | |
Locate handle functions | |
Copyright (c) 2006 - 2023, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "DxeMain.h" | |
#include "Handle.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 seach 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 * | |
CoreGetNextLocateAllHandles ( | |
IN OUT LOCATE_POSITION *Position, | |
OUT VOID **Interface | |
); | |
/** | |
Routine to get the next Handle, when you are searching for register protocol | |
notifies. | |
@param Position Information about which Handle to seach 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 * | |
CoreGetNextLocateByRegisterNotify ( | |
IN OUT LOCATE_POSITION *Position, | |
OUT VOID **Interface | |
); | |
/** | |
Routine to get the next Handle, when you are searching for a given protocol. | |
@param Position Information about which Handle to seach 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 * | |
CoreGetNextLocateByProtocol ( | |
IN OUT LOCATE_POSITION *Position, | |
OUT VOID **Interface | |
); | |
/** | |
Internal function for locating the requested handle(s) and returns them in Buffer. | |
The caller should already have acquired the ProtocolLock. | |
@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 | |
InternalCoreLocateHandle ( | |
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 = CoreGetNextLocateAllHandles; | |
break; | |
case ByRegisterNotify: | |
// | |
// Must have SearchKey for locate ByRegisterNotify | |
// | |
if (SearchKey == NULL) { | |
Status = EFI_INVALID_PARAMETER; | |
break; | |
} | |
GetNext = CoreGetNextLocateByRegisterNotify; | |
break; | |
case ByProtocol: | |
GetNext = CoreGetNextLocateByProtocol; | |
if (Protocol == NULL) { | |
Status = EFI_INVALID_PARAMETER; | |
break; | |
} | |
// | |
// Look up the protocol entry and set the head pointer | |
// | |
Position.ProtEntry = CoreFindProtocolEntry (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; | |
} | |
ASSERT (GetNext != NULL); | |
// | |
// 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)) { | |
// | |
// If this is a search by register notify and a handle was | |
// returned, update the register notification position | |
// | |
ASSERT (SearchKey != NULL); | |
ProtNotify = SearchKey; | |
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 | |
CoreLocateHandle ( | |
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; | |
// | |
// Lock the protocol database | |
// | |
CoreAcquireProtocolLock (); | |
Status = InternalCoreLocateHandle (SearchType, Protocol, SearchKey, BufferSize, Buffer); | |
CoreReleaseProtocolLock (); | |
return Status; | |
} | |
/** | |
Routine to get the next Handle, when you are searching for all handles. | |
@param Position Information about which Handle to seach 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 * | |
CoreGetNextLocateAllHandles ( | |
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 seach 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 * | |
CoreGetNextLocateByRegisterNotify ( | |
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 seach 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 * | |
CoreGetNextLocateByProtocol ( | |
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; | |
} | |
/** | |
Locates the handle to a device on the device path that supports the specified protocol. | |
@param Protocol Specifies the protocol to search for. | |
@param DevicePath On input, a pointer to a pointer to the device path. On output, the device | |
path pointer is modified to point to the remaining part of the device | |
path. | |
@param Device A pointer to the returned device handle. | |
@retval EFI_SUCCESS The resulting handle was returned. | |
@retval EFI_NOT_FOUND No handles match the search. | |
@retval EFI_INVALID_PARAMETER Protocol is NULL. | |
@retval EFI_INVALID_PARAMETER DevicePath is NULL. | |
@retval EFI_INVALID_PARAMETER A handle matched the search and Device is NULL. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
CoreLocateDevicePath ( | |
IN EFI_GUID *Protocol, | |
IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath, | |
OUT EFI_HANDLE *Device | |
) | |
{ | |
INTN SourceSize; | |
INTN Size; | |
INTN BestMatch; | |
UINTN HandleCount; | |
UINTN Index; | |
EFI_STATUS Status; | |
EFI_HANDLE *Handles; | |
EFI_HANDLE Handle; | |
EFI_HANDLE BestDevice; | |
EFI_DEVICE_PATH_PROTOCOL *SourcePath; | |
EFI_DEVICE_PATH_PROTOCOL *TmpDevicePath; | |
if (Protocol == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((DevicePath == NULL) || (*DevicePath == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Handles = NULL; | |
BestDevice = NULL; | |
SourcePath = *DevicePath; | |
TmpDevicePath = SourcePath; | |
while (!IsDevicePathEnd (TmpDevicePath)) { | |
if (IsDevicePathEndInstance (TmpDevicePath)) { | |
// | |
// If DevicePath is a multi-instance device path, | |
// the function will operate on the first instance | |
// | |
break; | |
} | |
TmpDevicePath = NextDevicePathNode (TmpDevicePath); | |
} | |
SourceSize = (UINTN)TmpDevicePath - (UINTN)SourcePath; | |
// | |
// Get a list of all handles that support the requested protocol | |
// | |
Status = CoreLocateHandleBuffer (ByProtocol, Protocol, NULL, &HandleCount, &Handles); | |
if (EFI_ERROR (Status) || (HandleCount == 0)) { | |
return EFI_NOT_FOUND; | |
} | |
BestMatch = -1; | |
for (Index = 0; Index < HandleCount; Index += 1) { | |
Handle = Handles[Index]; | |
Status = CoreHandleProtocol (Handle, &gEfiDevicePathProtocolGuid, (VOID **)&TmpDevicePath); | |
if (EFI_ERROR (Status)) { | |
// | |
// If this handle doesn't support device path, then skip it | |
// | |
continue; | |
} | |
// | |
// Check if DevicePath is first part of SourcePath | |
// | |
Size = GetDevicePathSize (TmpDevicePath) - sizeof (EFI_DEVICE_PATH_PROTOCOL); | |
ASSERT (Size >= 0); | |
if ((Size <= SourceSize) && (CompareMem (SourcePath, TmpDevicePath, (UINTN)Size) == 0)) { | |
// | |
// If the size is equal to the best match, then we | |
// have a duplicate device path for 2 different device | |
// handles | |
// | |
ASSERT (Size != BestMatch); | |
// | |
// We've got a match, see if it's the best match so far | |
// | |
if (Size > BestMatch) { | |
BestMatch = Size; | |
BestDevice = Handle; | |
} | |
} | |
} | |
CoreFreePool (Handles); | |
// | |
// If there wasn't any match, then no parts of the device path was found. | |
// Which is strange since there is likely a "root level" device path in the system. | |
// | |
if (BestMatch == -1) { | |
return EFI_NOT_FOUND; | |
} | |
if (Device == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
*Device = BestDevice; | |
// | |
// Return the remaining part of the device path | |
// | |
*DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)(((UINT8 *)SourcePath) + BestMatch); | |
return EFI_SUCCESS; | |
} | |
/** | |
Return the first Protocol Interface that matches the Protocol GUID. If | |
Registration is passed 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 | |
CoreLocateProtocol ( | |
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; | |
// | |
// Lock the protocol database | |
// | |
Status = CoreAcquireLockOrFail (&gProtocolDatabaseLock); | |
if (EFI_ERROR (Status)) { | |
return EFI_NOT_FOUND; | |
} | |
mEfiLocateHandleRequest += 1; | |
if (Registration == NULL) { | |
// | |
// Look up the protocol entry and set the head pointer | |
// | |
Position.ProtEntry = CoreFindProtocolEntry (Protocol, FALSE); | |
if (Position.ProtEntry == NULL) { | |
Status = EFI_NOT_FOUND; | |
goto Done; | |
} | |
Position.Position = &Position.ProtEntry->Protocols; | |
Handle = CoreGetNextLocateByProtocol (&Position, Interface); | |
} else { | |
Handle = CoreGetNextLocateByRegisterNotify (&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; | |
} | |
Done: | |
CoreReleaseProtocolLock (); | |
return Status; | |
} | |
/** | |
Function returns an array of handles that support the requested protocol | |
in a buffer allocated from pool. This is a version of CoreLocateHandle() | |
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 | |
CoreLocateHandleBuffer ( | |
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; | |
// | |
// Lock the protocol database | |
// | |
CoreAcquireProtocolLock (); | |
Status = InternalCoreLocateHandle ( | |
SearchType, | |
Protocol, | |
SearchKey, | |
&BufferSize, | |
*Buffer | |
); | |
// | |
// LocateHandleBuffer() returns incorrect status code if SearchType is | |
// invalid. | |
// | |
// Add code to correctly handle expected errors from CoreLocateHandle(). | |
// | |
if (EFI_ERROR (Status) && (Status != EFI_BUFFER_TOO_SMALL)) { | |
if (Status != EFI_INVALID_PARAMETER) { | |
Status = EFI_NOT_FOUND; | |
} | |
CoreReleaseProtocolLock (); | |
return Status; | |
} | |
*Buffer = AllocatePool (BufferSize); | |
if (*Buffer == NULL) { | |
CoreReleaseProtocolLock (); | |
return EFI_OUT_OF_RESOURCES; | |
} | |
Status = InternalCoreLocateHandle ( | |
SearchType, | |
Protocol, | |
SearchKey, | |
&BufferSize, | |
*Buffer | |
); | |
*NumberHandles = BufferSize / sizeof (EFI_HANDLE); | |
if (EFI_ERROR (Status)) { | |
*NumberHandles = 0; | |
if (*Buffer != NULL) { | |
CoreFreePool (*Buffer); | |
*Buffer = NULL; | |
} | |
} | |
CoreReleaseProtocolLock (); | |
return Status; | |
} |