Newer
Older
AMI-Aptio-BIOS-Reversed / DxeCore / Hand / Handle.c
@Ajax Dong Ajax Dong 2 days ago 35 KB Init
/** @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;
}