/** @file
UEFI DXE Foundation Handle Database
MdeModulePkg/Core/Dxe/Hand/Handle.c -- Protocol handle database management.
Decompiled from DxeCore.efi (0111_DxeCore_50b6b9c84d71).
Functions decompiled and renamed:
CoreIsHandleValid (0x4B80) -- Validate "hndl" signature
CoreGetProtocolName (0x4AA0) -- Binary search GUID->name table
CoreFindProtocolEntry (0x4BB0) -- Find/create PROTOCOL_ENTRY by GUID
CoreFindProtocolInterface (0x4CB0) -- Find PROTOCOL_INTERFACE on a handle
CoreUninstallProtocolInterface (0x4D6C) -- Remove all OPEN_PROTOCOL_INFO entries
CoreNotifyProtocolEntry (0x5204) -- Notify + disconnect for protocol changes
CoreFindProtocolInHandle (0x54E4) -- Search handle's protocol list by GUID
CoreInstallProtocolInterfaceNotify (0x5598) -- Internal protocol install helper
CoreProtocolsPerHandle (0x5B28) -- Get all protocol GUIDs on a handle
CoreInstallProtocolInterface (0x4E74) -- Public API for protocol installation
Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "../uefi_headers/Uefi.h"
//
// Signatures (confirmed from decompilation)
//
#define IHANDLE_SIGNATURE SIGNATURE_32('h','n','d','l') // 0x6C646E68 = 1818521192
#define PROTOCOL_ENTRY_SIGNATURE SIGNATURE_32('p','r','t','e') // 0x65747270 = 1702130288
#define PROTOCOL_INTERFACE_SIGNATURE SIGNATURE_32('p','o','d','l') // 0x6C646F70 = 1818521456
#define PROTOCOL_INTERFACE_EX_SIGNATURE SIGNATURE_32('p','i','f','c') // 0x63666970 = 1667656048
#define OPEN_PROTOCOL_INFO_SIGNATURE SIGNATURE_32('p','r','t','n') // 0x6E747270 = 1853125232
//
// Protocol attribute/type bits found in the Attributes field (+0x28 in
// PROTOCOL_INTERFACE, low 32 bits of QWORD at index 5).
//
#define PROTO_ATTR_EXCLUSIVE 0x10 // BIT4 -- exclusive open
#define PROTO_ATTR_DELETE_PENDING 0x20 // BIT5 -- deletion pending
//
// Interface-type constants (n4 parameter)
//
#define IF_TYPE_NATIVE 1
#define IF_TYPE_ADD_BY_HANDLE 8
#define IF_TYPE_NOTIFY_OPEN 16
#define IF_TYPE_NOTIFY_CLOSE 32
#define IF_TYPE_NOTIFY_SIGNAL 48
//
// Status shorthand
//
#define EFI_ALREADY_STARTED 0x8000000000000014LL
#define EFI_ACCESS_DENIED 0x800000000000000FLL
#define EFI_NOT_FOUND 0x8000000000000003LL
//=============================================================================
// Structure definitions (sizes derived from allocation sizes: 0x30, 0x38,
// 0x48, 0x58). Offsets are confirmed by the constant array indices used
// in the decompiled code.
//=============================================================================
//
// IHANDLE -- a handle in the UEFI handle database.
// Signature: "hndl"
// Allocation size: 0x38 bytes (56 bytes) = 7 QWORDs
//
// [ 0] +0x00: Signature (UINTN)
// [ 1] +0x08: AllHandles.Flink (link on gHandleList)
// [ 2] +0x10: AllHandles.Blink
// [ 3] +0x18: Protocols.Flink (list of PROTOCOL_INTERFACE_EX nodes)
// [ 4] +0x20: Protocols.Blink
// [ 5] +0x28: HandleIndex (UINTN -- not directly accessed in these funcs)
// [ 6] +0x30: Key (UINTN -- unique handle index)
//
typedef struct {
UINTN Signature;
LIST_ENTRY AllHandles;
LIST_ENTRY Protocols;
UINTN HandleIndex;
UINTN Key;
} IHANDLE;
//
// PROTOCOL_ENTRY -- one per protocol GUID registered in the database.
// Signature: "prte"
// Allocation size: 0x48 bytes (72 bytes) = 9 QWORDs
//
// [ 0] +0x00: Signature (UINTN)
// [ 1] +0x08: Link.Flink (link on gProtocolDatabase)
// [ 2] +0x10: Link.Blink
// [ 3] +0x18: ProtocolId.Data1 (EFI_GUID, 16 bytes = 2 QWORDs)
// [ 4] +0x20: ProtocolId.Data4[0..7]
// [ 5] +0x28: Protocols.Flink (list of child PROTOCOL_INTERFACE_EX nodes)
// [ 6] +0x30: Protocols.Blink
// [ 7] +0x38: Notify.Flink (list of OPEN_PROTOCOL_INFO / notify entries)
// [ 8] +0x40: Notify.Blink
//
typedef struct {
UINTN Signature;
LIST_ENTRY Link;
EFI_GUID ProtocolId;
LIST_ENTRY Protocols;
LIST_ENTRY Notify;
} PROTOCOL_ENTRY;
//
// PROTOCOL_INTERFACE (small form, 0x30 = 48 bytes) -- used for notification
// and attribute tracking of protocol interfaces. Signature: "podl".
// These nodes live on the OpenList of a PROTOCOL_INTERFACE_EX or on the
// Notify list of a PROTOCOL_ENTRY.
//
// Array-index access from the decompiler:
// [ 0] +0x00: Signature (UINT64) -- "podl"
// [ 1] +0x08: Link.Flink -- link on parent list
// [ 2] +0x10: Link.Blink
// [ 3] +0x18: Reference (VOID *) -- compared with ProtInterface param
// [ 4] +0x20: Interface (VOID *) -- compared with Registration param
// [ 5] +0x28: low DWORD = Attributes (UINT32)
// high DWORD = OpenCount (UINT32)
//
typedef struct {
UINTN Signature;
LIST_ENTRY Link;
VOID *Reference;
VOID *Interface;
UINT32 Attributes;
UINT32 OpenCount;
} PROTOCOL_INTERFACE;
//
// PROTOCOL_INTERFACE_EX (0x58 = 88 bytes) -- full protocol interface record
// on a handle. Signature: "pifc".
//
// Array-index access from the decompiler:
// [ 0] +0x00: Signature (UINT64) -- "pifc"
// [ 1] +0x08: HandleLink.Flink -- link on IHANDLE.Protocols
// [ 2] +0x10: HandleLink.Blink
// [ 3] +0x18: Handle (IHANDLE *) -- back-pointer to owning handle
// [ 4] +0x20: EntryLink.Flink -- link on PROTOCOL_ENTRY.Protocols
// [ 5] +0x28: EntryLink.Blink
// [ 6] +0x30: Protocol (PROTOCOL_ENTRY *) -- back-pointer to protocol entry
// [ 7] +0x38: Interface (VOID *) -- protocol interface pointer
// [ 8] +0x40: OpenList.Flink -- head of OPEN_PROTOCOL_INFO list
// [ 9] +0x48: OpenList.Blink
// [10] +0x50: OpenCount (UINT64)
//
typedef struct {
UINTN Signature;
LIST_ENTRY HandleLink;
IHANDLE *Handle;
LIST_ENTRY EntryLink;
PROTOCOL_ENTRY *Protocol;
VOID *Interface;
LIST_ENTRY OpenList;
UINT64 OpenCount;
} PROTOCOL_INTERFACE_EX;
//
// OPEN_PROTOCOL_INFO (0x30 bytes assumed, signature "prtn") -- tracks
// a single protocol open by an agent.
//
// Array-index access from decompiler (j - 2 backs up 2 QWORDs from Link):
// [ 0] +0x00: Signature (UINT64) -- "prtn"
// [ 1] +0x08: ??? -- unknown/padding
// [ 2] +0x10: Link.Flink -- link on parent list
// [ 3] +0x18: Link.Blink
// [ 4] +0x20: AgentHandle (UINTN) -- compared with Interface in CoreUIP_0
// [ 5] +0x28: ControllerHandle (UINTN)
// [ 6] +0x30: Attributes (UINT32)
// [ 7] +0x34: OpenCount (UINT32)
//
typedef struct {
UINTN Signature;
UINTN Unknown1;
LIST_ENTRY Link;
UINTN AgentHandle;
UINTN ControllerHandle;
UINT32 Attributes;
UINT32 OpenCount;
} OPEN_PROTOCOL_INFO;
//=============================================================================
// Forward declarations (resolved from DriverSupport.c / other modules)
//=============================================================================
EFI_STATUS
CoreDisconnectControllersFromProtocol (
IN IHANDLE *Handle,
IN PROTOCOL_INTERFACE_EX *Prot,
IN VOID *Interface OPTIONAL
);
EFI_STATUS
CoreNotifyProtocolEntry (
IN IHANDLE *Handle,
IN PROTOCOL_INTERFACE_EX *ProtInterface,
IN VOID *Data OPTIONAL,
IN UINTN Tpl
);
//=============================================================================
// Global protocol database (defined in DxeMain)
//=============================================================================
extern LIST_ENTRY gProtocolDatabase; // off_23078
extern LIST_ENTRY gHandleList; // off_25AD8
extern UINTN gHandleKeyCounter; // qword_26410
//=============================================================================
// Internal helpers (provided by DXE library / memory module)
//=============================================================================
extern VOID CoreAcquireProtocolLock (VOID);
extern VOID CoreReleaseProtocolLock (VOID);
extern BOOLEAN CompareGuid (IN EFI_GUID *, IN EFI_GUID *);
extern VOID CopyGuid (OUT EFI_GUID *, IN EFI_GUID *);
extern VOID *AllocateZeroPool (UINTN Size);
extern VOID FreePool (VOID *Buffer);
//=============================================================================
// CR macro -- container record from a LIST_ENTRY pointer
//=============================================================================
#ifndef CR
#define CR(Record, TYPE, Field, Signature) \
((TYPE *)((UINTN)(Record) - OFFSET_OF(TYPE, Field)))
#endif
//
// Lock-state constant matching DxeGetInfo_8/DxeGetInfo_10
//
#define LOCK_ACQUIRED 2
//=============================================================================
// Protocol-Name Table (binary-search target at 0x23090)
//=============================================================================
typedef struct {
UINT32 Data1;
UINT16 Data2;
UINT16 Data3;
UINT8 Data4[8];
CHAR8 *NameString;
} PROTOCOL_NAME_ENTRY;
extern PROTOCOL_NAME_ENTRY gProtocolNameTable[];
//=============================================================================
// CoreIsHandleValid
//=============================================================================
EFI_STATUS
EFIAPI
CoreIsHandleValid (
IN IHANDLE *Handle
)
{
if ((Handle != NULL) && (Handle->Signature == IHANDLE_SIGNATURE)) {
return EFI_SUCCESS;
}
return EFI_INVALID_PARAMETER;
}
//=============================================================================
// CoreGetProtocolName
//=============================================================================
CHAR8 *
EFIAPI
CoreGetProtocolName (
IN EFI_GUID *ProtocolGuid
)
{
INTN Low;
INTN High;
INTN Mid;
CHAR8 *Entry;
UINTN Index;
INTN Cmp;
Low = 0;
High = 449;
while (Low <= High) {
Mid = (High + Low) / 2;
Entry = (CHAR8 *)&gProtocolNameTable[Mid];
//
// Compare GUID field-by-field in order
//
if (*(UINT32 *)Entry != *(UINT32 *)ProtocolGuid) {
Cmp = (*(UINT32 *)Entry < *(UINT32 *)ProtocolGuid) ? -1 : 1;
} else {
//
// Data2 compare
//
if (*(UINT16 *)(Entry + 4) != *(UINT16 *)((UINT8 *)ProtocolGuid + 4)) {
Cmp = *(UINT16 *)(Entry + 4) - *(UINT16 *)((UINT8 *)ProtocolGuid + 4);
} else {
//
// Data3 compare
//
if (*(UINT16 *)(Entry + 6) != *(UINT16 *)((UINT8 *)ProtocolGuid + 6)) {
Cmp = *(UINT16 *)(Entry + 6) - *(UINT16 *)((UINT8 *)ProtocolGuid + 6);
} else {
//
// Data4 compare (8 bytes)
//
for (Index = 0; Index < 8; Index++) {
if (Entry[Index + 8] != ((CHAR8 *)ProtocolGuid)[Index + 8]) {
Cmp = (UINT8)Entry[Index + 8] - (UINT8)((CHAR8 *)ProtocolGuid)[Index + 8];
goto ContinueLoop;
}
}
//
// Full match -- return the name pointer at offset +16 in the table entry
//
return *(CHAR8 **)(Entry + 16);
}
}
}
ContinueLoop:
if (Cmp < 0) {
Low = Mid + 1;
} else if (Cmp > 0) {
High = Mid - 1;
} else {
return *(CHAR8 **)(Entry + 16);
}
}
return NULL;
}
//=============================================================================
// CoreFindProtocolEntry (Handle.c lines 128-165)
//=============================================================================
PROTOCOL_ENTRY *
EFIAPI
CoreFindProtocolEntry (
IN EFI_GUID *ProtocolGuid,
IN BOOLEAN Create
)
{
LIST_ENTRY *Link;
PROTOCOL_ENTRY *ProtEntry;
PROTOCOL_ENTRY *NewEntry;
//
// Lock must be held
//
ASSERT (gProtocolDatabaseLock.Lock == EfiLockAcquired);
//
// Walk the global protocol database
//
Link = gProtocolDatabase.ForwardLink;
while (Link != &gProtocolDatabase) {
ProtEntry = CR (Link, PROTOCOL_ENTRY, Link, PROTOCOL_ENTRY_SIGNATURE);
if (CompareGuid (&ProtEntry->ProtocolId, ProtocolGuid)) {
return ProtEntry;
}
Link = Link->ForwardLink;
}
//
// Create a new entry if requested
//
if (Create) {
NewEntry = AllocateZeroPool (sizeof (PROTOCOL_ENTRY));
if (NewEntry != NULL) {
NewEntry->Signature = PROTOCOL_ENTRY_SIGNATURE;
CopyGuid (&NewEntry->ProtocolId, ProtocolGuid);
InitializeListHead (&NewEntry->Protocols);
InitializeListHead (&NewEntry->Notify);
InsertTailList (&gProtocolDatabase, &NewEntry->Link);
}
return NewEntry;
}
return NULL;
}
//=============================================================================
// CoreFindProtocolInterface (Handle.c lines 200-236)
//=============================================================================
PROTOCOL_INTERFACE_EX *
EFIAPI
CoreFindProtocolInterface (
IN IHANDLE *Handle,
IN EFI_GUID *ProtocolGuid,
IN VOID *Interface
)
{
PROTOCOL_ENTRY *ProtEntry;
LIST_ENTRY *Link;
PROTOCOL_INTERFACE_EX *ProtInterface;
ASSERT (gProtocolDatabaseLock.Lock == EfiLockAcquired);
//
// Find the protocol entry by GUID, then walk Handle->Protocols looking for
// a matching interface.
//
ProtEntry = CoreFindProtocolEntry (ProtocolGuid, FALSE);
if (ProtEntry == NULL) {
return NULL;
}
Link = Handle->Protocols.ForwardLink;
while (Link != &Handle->Protocols) {
ProtInterface = CR (Link, PROTOCOL_INTERFACE_EX, HandleLink,
PROTOCOL_INTERFACE_EX_SIGNATURE);
if ((ProtInterface->Protocol == ProtEntry) &&
(ProtInterface->Interface == Interface)) {
return ProtInterface;
}
Link = Link->ForwardLink;
}
return NULL;
}
//=============================================================================
// CoreUninstallProtocolInterface (Handle.c lines 252-284)
//=============================================================================
//
// Internal helper that removes OPEN_PROTOCOL_INFO entries from the Notify
// list of every PROTOCOL_ENTRY where AgentHandle == Interface.
//
// Decompiler trace:
// Walks gProtocolDatabase. For each PROTOCOL_ENTRY (sig "prte", CR macro),
// walks the embedded Notify list at offset +0x38 (index 7). Each entry
// on Notify is an OPEN_PROTOCOL_INFO (sig "prtn") whose Link is at
// offset +0x10 (2 QWORDs from base). If AgentHandle (+0x20) matches,
// the entry is removed and freed. Loop repeats until no more matches.
//
EFI_STATUS
EFIAPI
CoreUninstallProtocolInterface (
IN UINTN Interface
)
{
LIST_ENTRY *DbLink;
PROTOCOL_ENTRY *ProtEntry;
LIST_ENTRY *NotifyEntry; // j: walks Notify list
LIST_ENTRY *NotifyHead; // j_1: address of Notify head
OPEN_PROTOCOL_INFO *Info; // j_2: the matched entry
for (;;) {
CoreAcquireProtocolLock ();
//
// Walk every PROTOCOL_ENTRY in the database
//
DbLink = gProtocolDatabase.ForwardLink;
while (DbLink != &gProtocolDatabase) {
ProtEntry = CR (DbLink, PROTOCOL_ENTRY, Link, PROTOCOL_ENTRY_SIGNATURE);
NotifyHead = &ProtEntry->Notify;
NotifyEntry = NotifyHead->ForwardLink;
while (NotifyEntry != NotifyHead) {
Info = (OPEN_PROTOCOL_INFO *)((UINTN)NotifyEntry - 16);
if (Info->Signature != OPEN_PROTOCOL_INFO_SIGNATURE) {
Info = (OPEN_PROTOCOL_INFO *)NotifyEntry;
}
if (Info->AgentHandle == Interface) {
RemoveEntryList (&Info->Link);
FreePool (Info);
CoreReleaseProtocolLock ();
goto NextIteration;
}
NotifyEntry = NotifyEntry->ForwardLink;
}
DbLink = DbLink->ForwardLink;
}
//
// No more entries match -- done
//
CoreReleaseProtocolLock ();
return EFI_SUCCESS;
NextIteration:
;
}
}
//=============================================================================
// CoreNotifyProtocolEntry (Handle.c lines 667-735)
//=============================================================================
//
// Called when a protocol is added to or removed from a PROTOCOL_ENTRY.
// The second parameter (a2) is a PROTOCOL_INTERFACE_EX whose OpenList
// is at a2+0x40 and OpenCount at a2+0x50.
//
// Phase 1: Walk OpenList. For each PROTOCOL_INTERFACE (sig "podl"),
// if Attributes has BIT4 (exclusive), disconnect controllers
// from that protocol node.
// Phase 2: Walk OpenList again. If Attributes has any of the low 3 bits
// set (BY_HANDLE / GET_PROTOCOL / TEST_PROTOCOL), remove the
// entry and decrement OpenCount.
// Phase 3: If OpenCount != 0, disconnect the controller and return
// EFI_ACCESS_DENIED.
//
EFI_STATUS
EFIAPI
CoreNotifyProtocolEntry (
IN IHANDLE *Handle,
IN PROTOCOL_INTERFACE_EX *ProtInterface,
IN VOID *Data OPTIONAL,
IN UINTN Tpl
)
{
LIST_ENTRY *OpenList;
LIST_ENTRY *Link;
PROTOCOL_INTERFACE *Node;
EFI_STATUS Status;
LIST_ENTRY *DelLink;
OpenList = &ProtInterface->OpenList;
Status = EFI_SUCCESS;
//
// Phase 1: Disconnect controllers from exclusive protocol interfaces.
// If the disconnect succeeds, retry from the top of the list.
// If the disconnect fails, skip to Phase 3 (LABEL_18).
//
RetryDisconnect:
Link = OpenList->ForwardLink;
while (Link != OpenList) {
Node = CR (Link, PROTOCOL_INTERFACE, Link, PROTOCOL_INTERFACE_SIGNATURE);
if (Node->Attributes & PROTO_ATTR_EXCLUSIVE) {
CoreReleaseProtocolLock ();
Status = CoreDisconnectControllersFromProtocol (
Handle,
(PROTOCOL_INTERFACE_EX *)Node->Reference,
NULL
);
CoreAcquireProtocolLock ();
if (!EFI_ERROR (Status)) {
goto RetryDisconnect;
}
//
// Disconnect failed -- skip Phase 2, go directly to Phase 3
//
goto Phase3;
}
Link = Link->ForwardLink;
}
//
// Phase 2: Remove entries with BY_HANDLE, GET_PROTOCOL, or TEST_PROTOCOL
// attributes (low 3 bits = BIT0 | BIT1 | BIT2).
//
Link = OpenList->ForwardLink;
while (Link != OpenList) {
Node = CR (Link, PROTOCOL_INTERFACE, Link, PROTOCOL_INTERFACE_SIGNATURE);
if (Node->Attributes & (BIT0 | BIT1 | BIT2)) {
DelLink = RemoveEntryList (&Node->Link);
ProtInterface->OpenCount--;
Link = DelLink;
FreePool (Node);
} else {
Link = Link->ForwardLink;
}
}
//
// Phase 3: If openers remain, disconnect the controller and return DENIED
//
Phase3:
if (ProtInterface->OpenCount != 0) {
CoreReleaseProtocolLock ();
CoreDisconnectControllersFromProtocol (Handle, NULL, NULL);
CoreAcquireProtocolLock ();
return EFI_ACCESS_DENIED;
}
return Status;
}
//=============================================================================
// CoreFindProtocolInHandle (Handle.c lines 930-954)
//=============================================================================
//
// Walks Handle->Protocols (at +0x18, index 3 in IHANDLE). Each entry is a
// PROTOCOL_INTERFACE_EX (sig "pifc") whose Protocol field (+0x30) points to
// a PROTOCOL_ENTRY. Returns the matching PROTOCOL_INTERFACE_EX or NULL.
// Callers then use ProtEx->Protocol to get the PROTOCOL_ENTRY.
//
PROTOCOL_INTERFACE_EX *
EFIAPI
CoreFindProtocolInHandle (
IN IHANDLE *Handle,
IN EFI_GUID *ProtocolGuid
)
{
LIST_ENTRY *Link;
PROTOCOL_INTERFACE_EX *ProtEx;
if (EFI_ERROR (CoreIsHandleValid (Handle))) {
return NULL;
}
Link = Handle->Protocols.ForwardLink;
while (Link != &Handle->Protocols) {
ProtEx = CR (Link, PROTOCOL_INTERFACE_EX, HandleLink,
PROTOCOL_INTERFACE_EX_SIGNATURE);
if (CompareGuid (
&ProtEx->Protocol->ProtocolId,
ProtocolGuid
)) {
return ProtEx;
}
Link = Link->ForwardLink;
}
return NULL;
}
//=============================================================================
// CoreProtocolsPerHandle (Handle.c lines 1500-1530)
//=============================================================================
EFI_STATUS
EFIAPI
CoreProtocolsPerHandle (
IN IHANDLE *Handle,
OUT EFI_GUID **ProtocolBuffer,
OUT UINTN *ProtocolCount
)
{
EFI_STATUS Status;
LIST_ENTRY *Link;
UINTN Count;
PROTOCOL_INTERFACE_EX *ProtInterface;
EFI_GUID **Buffer;
Status = CoreIsHandleValid (Handle);
if (EFI_ERROR (Status)) {
return Status;
}
if ((ProtocolBuffer == NULL) || (ProtocolCount == NULL)) {
return EFI_INVALID_PARAMETER;
}
*ProtocolBuffer = NULL;
*ProtocolCount = 0;
CoreAcquireProtocolLock ();
//
// Count protocols on this handle
//
Link = Handle->Protocols.ForwardLink;
Count = 0;
while (Link != &Handle->Protocols) {
Link = Link->ForwardLink;
Count++;
}
if (Count == 0) {
Status = EFI_INVALID_PARAMETER;
goto Done;
}
Buffer = AllocateZeroPool (Count * sizeof (EFI_GUID *));
if (Buffer == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto Done;
}
*ProtocolBuffer = Buffer;
*ProtocolCount = Count;
//
// Fill buffer with protocol GUID pointers
//
Link = Handle->Protocols.ForwardLink;
while (Link != &Handle->Protocols) {
ProtInterface = CR (Link, PROTOCOL_INTERFACE_EX, HandleLink,
PROTOCOL_INTERFACE_EX_SIGNATURE);
*Buffer = &ProtInterface->Protocol->ProtocolId;
Buffer++;
Link = Link->ForwardLink;
}
Status = EFI_SUCCESS;
Done:
CoreReleaseProtocolLock ();
return Status;
}
//=============================================================================
// CoreInstallProtocolInterfaceNotify (Handle.c lines 1090-1230)
//=============================================================================
//
// Internal helper implementing the core protocol-database manipulation
// logic for protocol installation. Handles parameter validation by
// interface type, searches for existing protocol instances, and either
// increments the open count or installs a new PROTOCOL_INTERFACE node.
//
// Parameter mapping from decompiler:
// _ = Handle (1st, RCX)
// a2 = Protocol GUID (2nd, RDX)
// a3 = Interface (3rd, R8, output pointer)
// _a_2 = ProtInterface (4th, R9, registration handle / EX record)
// _a = Registration (5th, stack)
// n4 = InterfaceType (6th, stack)
//
// ProtInterface (4th param) acts as:
// IF_TYPE_NATIVE (1): NULL (caller provides no additional info)
// IF_TYPE_ADD_BY_HANDLE (8): validated as IHANDLE*
// IF_TYPE_NOTIFY_OPEN (16): validated as IHANDLE* (registration handle)
// IF_TYPE_NOTIFY_CLOSE (32): treated as IHANDLE* (like 16, validated)
// IF_TYPE_NOTIFY_SIGNAL (48):validated, then _a (5th) also as IHANDLE*
//
EFI_STATUS
EFIAPI
CoreInstallProtocolInterfaceNotify (
IN OUT IHANDLE *Handle,
IN EFI_GUID *Protocol,
OUT VOID **Interface,
IN PROTOCOL_INTERFACE_EX *ProtInterface,
IN VOID *Registration,
IN UINT32 InterfaceType
)
{
EFI_STATUS Status;
VOID **OutParam;
PROTOCOL_INTERFACE_EX *ProtEx; // v16 / v19
LIST_ENTRY *EntryList; // i_2 = &ProtEx->OpenList
LIST_ENTRY *Link; // i_3, i -- walks OpenList
PROTOCOL_INTERFACE *Node; // i_4, i_1
BOOLEAN HasExclusive;
BOOLEAN HasDelete;
PROTOCOL_INTERFACE *NewNode;
OutParam = Interface;
//
// Basic parameter validation
//
if ((Protocol == NULL) || ((InterfaceType != 4) && (Interface == NULL))) {
return EFI_INVALID_PARAMETER;
}
Status = CoreIsHandleValid (Handle);
if (Status < 0) {
return Status;
}
if (InterfaceType == 0) {
return EFI_INVALID_PARAMETER;
}
//
// InterfaceType 1, 2, or 4 -- no further handle validation needed
//
if ((InterfaceType <= 2) || (InterfaceType == 4)) {
goto AcquireLock;
}
if (InterfaceType != 8) {
//
// InterfaceType 16, 48, or 32
//
if (InterfaceType != 16) {
if (InterfaceType == 32) {
Registration = (VOID *)ProtInterface;
goto ValidateRegHandle;
}
if (InterfaceType != 48) {
return EFI_INVALID_PARAMETER;
}
}
//
// InterfaceType 48 (or 16): validate ProtInterface as handle
//
Status = CoreIsHandleValid ((IHANDLE *)ProtInterface);
if (Status < 0) {
return Status;
}
Registration = (VOID *)Registration;
goto ValidateRegHandle;
}
//
// InterfaceType == 8: both ProtInterface and Registration must be handles
//
Status = CoreIsHandleValid ((IHANDLE *)ProtInterface);
if (Status < 0) {
return Status;
}
Status = CoreIsHandleValid ((IHANDLE *)Registration);
if (Status < 0) {
return Status;
}
if (Handle == Registration) {
return EFI_INVALID_PARAMETER;
}
goto AcquireLock;
ValidateRegHandle:
Status = CoreIsHandleValid ((IHANDLE *)Registration);
if (Status < 0) {
return Status;
}
AcquireLock:
CoreAcquireProtocolLock ();
//
// Find the PROTOCOL_INTERFACE_EX for this protocol on this handle
//
ProtEx = CoreFindProtocolInHandle (Handle, Protocol);
if (ProtEx != NULL) {
HasExclusive = FALSE;
HasDelete = FALSE;
//
// Walk OpenList of the EX record (at ProtEx + 0x40 = &ProtEx->OpenList).
// Each entry is a PROTOCOL_INTERFACE (sig "podl").
//
EntryList = &ProtEx->OpenList;
Link = EntryList->ForwardLink;
while (Link != EntryList) {
Node = CR (Link, PROTOCOL_INTERFACE, Link, PROTOCOL_INTERFACE_SIGNATURE);
//
// Check if this node matches the parameters being installed.
// Reference field (+0x18) is compared with ProtInterface (4th param).
// Attributes field (+0x28) is compared with InterfaceType (6th param).
// Interface field (+0x20) is compared with Registration (5th param).
//
if ((Node->Reference == (VOID *)ProtInterface) &&
(Node->Attributes == InterfaceType) &&
(Node->Interface == Registration)) {
if (Node->Attributes & PROTO_ATTR_EXCLUSIVE) {
HasExclusive = TRUE;
Status = EFI_ALREADY_STARTED;
goto ReleaseLockReturn;
}
}
if (Node->Attributes & PROTO_ATTR_EXCLUSIVE) {
HasExclusive = TRUE;
}
if (Node->Attributes & PROTO_ATTR_DELETE_PENDING) {
HasDelete = TRUE;
}
Link = Link->ForwardLink;
}
//
// Check notify-type restrictions
//
if (InterfaceType == IF_TYPE_NOTIFY_OPEN) {
if (HasDelete || HasExclusive) {
Status = EFI_ACCESS_DENIED;
goto ReleaseLockReturn;
}
} else if (((InterfaceType - 32) & 0xFFFFFFEF) == 0) {
//
// InterfaceType 32 or 48 (notify close/signal types)
//
if (HasDelete) {
Status = EFI_ACCESS_DENIED;
goto ReleaseLockReturn;
}
if (HasExclusive) {
//
// Try to disconnect controllers from exclusive protocols
//
Link = EntryList->ForwardLink;
while (Link != EntryList) {
Node = CR (Link, PROTOCOL_INTERFACE, Link, PROTOCOL_INTERFACE_SIGNATURE);
if (Node->Attributes & PROTO_ATTR_EXCLUSIVE) {
CoreReleaseProtocolLock ();
Status = CoreDisconnectControllersFromProtocol (
Handle,
(PROTOCOL_INTERFACE_EX *)Node->Reference,
NULL
);
CoreAcquireProtocolLock ();
if (Status >= 0) {
goto RecheckExclusive;
}
goto ReleaseLockReturn;
}
Link = Link->ForwardLink;
}
}
goto AllocateNode;
}
}
//
// If ProtEx is NULL, no protocol entry exists on this handle at all.
// Return NOT_FOUND.
//
if (ProtEx == NULL) {
Status = EFI_NOT_FOUND;
goto ReleaseLockReturn;
}
//
// ProtEx exists but no matching node was found on OpenList.
// If ProtInterface is NULL, there is no registration handle to install
// (e.g. a native-type install that is just checking) -- return SUCCESS.
//
if (ProtInterface == NULL) {
Status = EFI_SUCCESS;
goto ReleaseLockReturn;
}
AllocateNode:
//
// Allocate a new PROTOCOL_INTERFACE (0x30 bytes) and link it onto
// the ProtEx->OpenList. Increment the EX record's OpenCount.
//
NewNode = AllocateZeroPool (sizeof (PROTOCOL_INTERFACE));
if (NewNode != NULL) {
NewNode->Signature = PROTOCOL_INTERFACE_SIGNATURE;
NewNode->Reference = (VOID *)ProtInterface;
NewNode->Interface = Registration;
NewNode->Attributes = InterfaceType;
NewNode->OpenCount = 1;
InsertTailList (&ProtEx->OpenList, &NewNode->Link);
ProtEx->OpenCount++;
Status = EFI_SUCCESS;
} else {
Status = EFI_OUT_OF_RESOURCES;
}
ReleaseLockReturn:
//
// For non-type-4, set the output interface pointer.
// On success (or ALREADY_STARTED), return the Interface field from the
// ProtEx record (at +0x38, QWORD index 7).
// On NOT_FOUND, return NULL.
//
if (InterfaceType != 4) {
if (!EFI_ERROR (Status) || (Status == EFI_ALREADY_STARTED)) {
if (ProtEx == NULL) {
//
// ASSERT: Prot != NULL (Handle.c line 1212)
//
} else {
*OutParam = ProtEx->Interface;
}
} else if (Status == EFI_NOT_FOUND) {
*OutParam = NULL;
}
}
CoreReleaseProtocolLock ();
return Status;
RecheckExclusive:
Link = EntryList->ForwardLink;
while (Link != EntryList) {
Node = CR (Link, PROTOCOL_INTERFACE, Link, PROTOCOL_INTERFACE_SIGNATURE);
if (Node->Attributes & PROTO_ATTR_EXCLUSIVE) {
CoreReleaseProtocolLock ();
Status = CoreDisconnectControllersFromProtocol (
Handle,
(PROTOCOL_INTERFACE_EX *)Node->Reference,
NULL
);
CoreAcquireProtocolLock ();
if (Status >= 0) {
goto AllocateNode;
}
goto ReleaseLockReturn;
}
Link = Link->ForwardLink;
}
goto AllocateNode;
}
//=============================================================================
// CoreInstallProtocolInterface (Handle.c public API)
//=============================================================================
//
// UEFI API: Installs a protocol interface on a handle. If *Handle is NULL,
// a new handle is created. The function:
// 1. Validates parameters
// 2. Debug-prints the protocol name
// 3. Tries a notify-only path if handle already exists
// 4. Finds/creates the PROTOCOL_ENTRY
// 5. Creates or reuses the IHANDLE
// 6. Creates and links a PROTOCOL_INTERFACE_EX record
// 7. Inserts into handle's protocol list (InsertHeadList)
// 8. Inserts into protocol entry's list (InsertTailList)
// 9. Calls CoreNotifyProtocolEntry
//
// Parameter mapping from decompiler:
// p__ = Handle (1st, char** output handle)
// dst = Protocol (2nd, GUID)
// a3 = Type (3rd, must be 0)
// p = Interface (4th, VOID*)
// a5 = NotifyFlag (5th, stack, BOOLEAN)
//
EFI_STATUS
EFIAPI
CoreInstallProtocolInterface (
IN OUT EFI_HANDLE *Handle,
IN EFI_GUID *Protocol,
IN EFI_INTERFACE_TYPE Type,
IN VOID *Interface
)
{
EFI_STATUS Status;
CHAR8 *ProtoName;
PROTOCOL_INTERFACE_EX *NewRecord;
IHANDLE *HandleImpl;
if ((Handle == NULL) || (Protocol == NULL) || (Type != 0)) {
return EFI_INVALID_PARAMETER;
}
//
// Debug: print protocol name
//
ProtoName = CoreGetProtocolName (Protocol);
if (ProtoName != NULL) {
DEBUG ((EFI_D_INFO, "InstallProtocolInterface: %a %p\n", ProtoName, Interface));
} else {
DEBUG ((EFI_D_INFO, "InstallProtocolInterface: %g %p\n", Protocol, Interface));
}
NewRecord = NULL;
HandleImpl = NULL;
Status = EFI_OUT_OF_RESOURCES;
//
// Quick check: if handle exists, try the notify-only path
//
if (*Handle != NULL) {
Status = CoreInstallProtocolInterfaceNotify (
(IHANDLE *)*Handle,
Protocol,
NULL,
NULL,
NULL,
IF_TYPE_NATIVE
);
if (!EFI_ERROR (Status)) {
//
// Protocol already registered via notify -- return ALREADY_STARTED
//
return EFI_ALREADY_STARTED;
}
}
CoreAcquireProtocolLock ();
//
// Find or create the PROTOCOL_ENTRY for this GUID
//
{
PROTOCOL_ENTRY *ProtEntry;
ProtEntry = CoreFindProtocolEntry (Protocol, TRUE);
if (ProtEntry != NULL) {
//
// Allocate the full PROTOCOL_INTERFACE_EX (0x58 bytes)
//
NewRecord = AllocateZeroPool (sizeof (PROTOCOL_INTERFACE_EX));
if (NewRecord != NULL) {
HandleImpl = (IHANDLE *)*Handle;
if (*Handle != NULL) {
Status = CoreIsHandleValid (HandleImpl);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR,
"InstallProtocolInterface: input handle at 0x%x is invalid\n",
(UINTN)HandleImpl));
goto Done;
}
} else {
//
// Create a new IHANDLE
//
HandleImpl = AllocateZeroPool (sizeof (IHANDLE));
if (HandleImpl == NULL) {
goto Done;
}
HandleImpl->Signature = IHANDLE_SIGNATURE;
InitializeListHead (&HandleImpl->Protocols);
HandleImpl->Key = ++gHandleKeyCounter;
InsertTailList (&gHandleList, &HandleImpl->AllHandles);
}
//
// Verify the protocol+interface isn't already installed
//
{
PROTOCOL_INTERFACE_EX *Existing;
Existing = CoreFindProtocolInterface (HandleImpl, Protocol, Interface);
//
// ASSERT (Handle.c line 474):
// Existing == NULL (must not be already present)
//
ASSERT (Existing == NULL);
}
//
// Initialize the PROTOCOL_INTERFACE_EX
//
NewRecord->Signature = PROTOCOL_INTERFACE_EX_SIGNATURE;
NewRecord->Handle = HandleImpl;
NewRecord->Protocol = ProtEntry;
NewRecord->Interface = Interface;
InitializeListHead (&NewRecord->OpenList);
NewRecord->OpenCount = 0;
//
// Link into handle's protocol list (InsertHeadList)
//
InsertHeadList (&HandleImpl->Protocols, &NewRecord->HandleLink);
//
// Link into protocol entry's protocol list (InsertTailList)
//
InsertTailList (&ProtEntry->Protocols, &NewRecord->EntryLink);
//
// Notify protocol listeners
//
CoreNotifyProtocolEntry (HandleImpl, NewRecord, NULL, 0);
Status = EFI_SUCCESS;
}
}
}
Done:
CoreReleaseProtocolLock ();
if (EFI_ERROR (Status)) {
if (NewRecord != NULL) {
FreePool (NewRecord);
}
DEBUG ((EFI_D_ERROR,
"InstallProtocolInterface: %g %p failed with %r\n",
Protocol, Interface, Status));
} else {
*Handle = (EFI_HANDLE)HandleImpl;
}
return Status;
}