Newer
Older
AMI-Aptio-BIOS-Reversed / DataHubDxe / DataHubDxe.c
@Ajax Dong Ajax Dong 2 days ago 34 KB Init
/** @file
  DataHubDxe.c -- UEFI Data Hub Protocol driver

  This DXE driver registers the EFI_DATA_HUB_PROTOCOL on the system's
  boot services protocol database. Callers can log data records by GUID,
  iterate over them with filters, and register/unregister data classes.

  Source: IntelFrameworkModulePkg/Universal/DataHubDxe/DataHub.c
  Compiled with: DEBUG_VS2015, X64, /O1

  File layout (relative to image base 0):
    .text  0x280 - 0x124C   (functions)
    .rdata 0x1260 - 0x183C   (strings, debug messages)
    .data  0x1960 - 0x1A48   (globals, protocol instance, lists)
**/

#include "DataHubDxe.h"

//
// ---------------------------------------------------------------------------
// External globals provided by UEFI boot/runtime services tables
// (populated by the EDK2 BaseLib constructor in ModuleEntryPoint)
// ---------------------------------------------------------------------------
//
extern EFI_SYSTEM_TABLE          *gST;
extern EFI_BOOT_SERVICES         *gBS;
extern EFI_RUNTIME_SERVICES      *gRT;
extern EFI_HANDLE                gImageHandle;

//
// ---------------------------------------------------------------------------
// Module globals (reside in .data section)
// ---------------------------------------------------------------------------
//
static CONST EFI_GUID  gEfiDataHubProtocolGuid =
  { 0xAE80D021, 0x618E, 0x11D4, { 0xBC, 0xD7, 0x00, 0x80, 0xC7, 0x3C, 0x88, 0x81 } };

//
// Private driver instance (DATA_HUB_PRIVATE_DATA)
//
static DATA_HUB_PRIVATE_DATA  mPrivateData;

//
// Function pointers for DataHub protocol methods installed in
// the protocol structure (indexed from mPrivateData.DataHubProtocol).
// These are stored separately and referenced by the protocol dispatch.
//
STATIC VOID                   *mHobList;            // qword_19C8
STATIC VOID                   *mSwitchHobList;      // qword_19C0
STATIC EFI_EVENT               mDataHubEvent;       // qword_19E8
STATIC UINT8                   mCmosIndex;          // qword_19D0 (not set at init)

//
// mPrivateData fields layout:
//   0x00  Signature       (set to DATA_HUB_PRIVATE_SIGNATURE = 0x4453424C "LBSD")
//   0x04  Reserved
//   0x08  EFI_DATA_HUB_PROTOCOL  (4 function pointers + protocol header)
//         0x08 LogData            -> DataHubLogData
//         0x10 GetNextDataRecord  -> DataHubGetNextDataRecord
//         0x18 RegisterDataClass  -> DataHubRegisterDataClass
//         0x20 UnregisterDataClass-> DataHubUnregisterDataClass
//   0x30  EFI_LOCK DataLock
//         0x30 Tpl                 (initialised to 16)
//         0x38 OwnerTpl
//         0x40 Lock               (initialised to 1 = EfiLockReleased)
//   0x48  TotalMonotonicCount      (running count)
//   0x50  DataRecordListHead       (self-referential LIST_ENTRY)
//         0x50 Flink -> 0x50
//         0x58 Blink -> 0x50
//   0x60  DataClassListHead        (self-referential LIST_ENTRY)
//         0x60 Flink -> 0x60
//         0x68 Blink -> 0x60
//   0x70  Reserved / extra fields
//

//
// ---------------------------------------------------------------------------
// Private helper: Get the DATA_HUB_PRIVATE_DATA* from a protocol* pointer
// using the CR macro pattern.
// ---------------------------------------------------------------------------
//
#define DATA_HUB_PRIVATE_DATA_FROM_PROTOCOL(This) \
  CR (This, DATA_HUB_PRIVATE_DATA, DataHubProtocol, DATA_HUB_PRIVATE_SIGNATURE)

#define DATA_HUB_RECORD_FROM_LINK(Link) \
  CR (Link, DATA_HUB_RECORD, RecordListEntry, DATA_HUB_RECORD_SIGNATURE)

#define DATA_HUB_CLASS_FROM_LINK(Link) \
  CR (Link, DATA_HUB_CLASS, ClassListEntry, DATA_HUB_CLASS_SIGNATURE)


//
// ---------------------------------------------------------------------------
// InternalBaseLibIsListValid ()
// ---------------------------------------------------------------------------
//
CHAR8
EFIAPI
InternalBaseLibIsListValid (
  IN LIST_ENTRY   *ListHead
  )
{
  if (ListHead == NULL) {
    DebugAssert ("e:\\hs\\MdePkg\\Library\\BaseLib\\LinkedList.c", 0x50u, "List != ((void *) 0)");
  }
  if (ListHead->Flink == NULL) {
    DebugAssert ("e:\\hs\\MdePkg\\Library\\BaseLib\\LinkedList.c", 0x51u, "List->ForwardLink != ((void *) 0)");
  }
  if (ListHead->Blink == NULL) {
    DebugAssert ("e:\\hs\\MdePkg\\Library\\BaseLib\\LinkedList.c", 0x52u, "List->BackLink != ((void *) 0)");
  }
  return TRUE;
}


//
// ---------------------------------------------------------------------------
// InsertTailList ()
//
// Insert Entry at the tail of the doubly-linked list headed by ListHead.
// Matches BaseLib's InsertTailList().
// ---------------------------------------------------------------------------
//
LIST_ENTRY *
EFIAPI
InsertTailList (
  IN LIST_ENTRY  *ListHead,
  IN LIST_ENTRY  *Entry
  )
{
  if (!InternalBaseLibIsListValid (ListHead)) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\BaseLib\\LinkedList.c",
      0x111u,
      "InternalBaseLibIsListValid (ListHead)"
      );
  }

  Entry->Flink              = ListHead;
  Entry->Blink              = ListHead->Blink;
  ListHead->Blink->Flink    = Entry;
  ListHead->Blink           = Entry;

  return ListHead;
}


//
// ---------------------------------------------------------------------------
// GetFirstNode ()
//
// Returns the first node in List (List->Flink).
// ---------------------------------------------------------------------------
//
LIST_ENTRY *
EFIAPI
GetFirstNode (
  IN CONST LIST_ENTRY  *List
  )
{
  if (!InternalBaseLibIsListValid (List)) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\BaseLib\\LinkedList.c",
      0x137u,
      "InternalBaseLibIsListValid (List)"
      );
  }
  return List->Flink;
}


//
// ---------------------------------------------------------------------------
// GetNextNode ()
//
// Returns the node after Node in List.
// ---------------------------------------------------------------------------
//
LIST_ENTRY *
EFIAPI
GetNextNode (
  IN CONST LIST_ENTRY  *List,
  IN CONST LIST_ENTRY  *Node
  )
{
  if (!InternalBaseLibIsListValid (List)) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\BaseLib\\LinkedList.c",
      0x15Bu,
      "InternalBaseLibIsListValid (List)"
      );
  }
  return Node->Flink;
}


//
// ---------------------------------------------------------------------------
// RemoveEntryList ()
//
// Unlinks Entry from its doubly-linked list.
// ---------------------------------------------------------------------------
//
LIST_ENTRY *
EFIAPI
RemoveEntryList (
  IN CONST LIST_ENTRY  *Entry
  )
{
  if (!InternalBaseLibIsListValid (Entry)) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\BaseLib\\LinkedList.c",
      0x1A0u,
      "InternalBaseLibIsListValid (ListHead)"
      );
  }
  if (Entry->Flink == Entry) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\BaseLib\\LinkedList.c",
      0x258u,
      "!IsListEmpty (Entry)"
      );
  }

  Entry->Flink->Blink = Entry->Blink;
  Entry->Blink->Flink = Entry->Flink;

  return Entry->Flink;
}


//
// ---------------------------------------------------------------------------
// CopyMem ()
//
// Memory copy wrapper. Delegates to InternalCopyMem for the actual copy
// after checking for overlap and validating pointers.
// ---------------------------------------------------------------------------
//
VOID *
EFIAPI
CopyMem (
  VOID          *DestinationBuffer,
  const VOID    *SourceBuffer,
  UINTN         Length
  )
{
  UINTN  V3;

  V3 = Length - 1;
  if (Length - 1 > (UINTN)(-1 - (INTN)DestinationBuffer)) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\BaseMemoryLibRepStr\\CopyMemWrapper.c",
      0x38u,
      "(Length - 1) <= (0xFFFFFFFFFFFFFFFFULL - (UINTN)DestinationBuffer)"
      );
  }
  if (V3 > (UINTN)(-1 - (INTN)SourceBuffer)) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\BaseMemoryLibRepStr\\CopyMemWrapper.c",
      0x39u,
      "(Length - 1) <= (0xFFFFFFFFFFFFFFFFULL - (UINTN)SourceBuffer)"
      );
  }
  if (DestinationBuffer == SourceBuffer) {
    return DestinationBuffer;
  }
  return InternalCopyMem ((CHAR8 *)DestinationBuffer, (CHAR8 *)SourceBuffer, Length);
}


//
// ---------------------------------------------------------------------------
// ZeroMem ()
//
// Memory zeroing wrapper. Delegates to InternalZeroMem.
// ---------------------------------------------------------------------------
//
VOID *
EFIAPI
ZeroMem (
  VOID          *Buffer,
  UINTN         Length
  )
{
  if (Buffer == NULL) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\BaseMemoryLibRepStr\\ZeroMemWrapper.c",
      0x35u,
      "Buffer != ((void *) 0)"
      );
  }
  if (Length > (UINTN)(-1 - (INTN)Buffer + 1)) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\BaseMemoryLibRepStr\\ZeroMemWrapper.c",
      0x36u,
      "Length <= (0xFFFFFFFFFFFFFFFFULL - (UINTN)Buffer + 1)"
      );
  }
  return InternalZeroMem ((CHAR8 *)Buffer, Length);
}


//
// ---------------------------------------------------------------------------
// CompareGuid ()
//
// Compares two GUIDs by comparing their 64-bit halves.
// ---------------------------------------------------------------------------
//
BOOLEAN
EFIAPI
CompareGuid (
  const GUID    *Guid1,
  const GUID    *Guid2
  )
{
  UINT64  Data1_1;
  UINT64  Data1_2;
  UINT64  Data4_1;
  UINT64  Data4_2;

  if (Guid1 == NULL || Guid2 == NULL) {
    return FALSE;
  }

  Data1_1 = ReadUnaligned64 ((const UINT64 *)&Guid1->Data1);
  Data1_2 = ReadUnaligned64 ((const UINT64 *)&Guid2->Data1);
  Data4_1 = ReadUnaligned64 ((const UINT64 *)Guid1->Data4);
  Data4_2 = ReadUnaligned64 ((const UINT64 *)Guid2->Data4);

  return (Data1_1 == Data1_2 && Data4_1 == Data4_2);
}


//
// ---------------------------------------------------------------------------
// AllocatePool ()
//
// Allocate boot-services memory pool of type EfiBootServicesData.
// ---------------------------------------------------------------------------
//
VOID *
EFIAPI
AllocatePool (
  UINTN         AllocationSize
  )
{
  EFI_STATUS  Status;
  VOID        *Buffer;

  Status = gBS->AllocatePool (EfiBootServicesData, AllocationSize, &Buffer);
  if (EFI_ERROR (Status)) {
    return NULL;
  }
  return Buffer;
}


//
// ---------------------------------------------------------------------------
// FreePool ()
//
// Free a memory pool allocation. Asserts on error.
// ---------------------------------------------------------------------------
//
VOID
EFIAPI
FreePool (
  VOID          *Buffer
  )
{
  EFI_STATUS  Status;

  Status = gBS->FreePool (Buffer);
  if (EFI_ERROR (Status)) {
    DebugPrintWithCmosCheck (
      EFI_D_ERROR,
      "\nASSERT_EFI_ERROR (Status = %r)\n",
      Status
      );
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\UefiMemoryAllocationLib\\MemoryAllocationLib.c",
      0x333u,
      "!EFI_ERROR (Status)"
      );
  }
}


//
// ---------------------------------------------------------------------------
// ReadUnaligned64 ()
//
// Read a UINT64 from an unaligned pointer.
// ---------------------------------------------------------------------------
//
UINT64
EFIAPI
ReadUnaligned64 (
  const UINT64  *Buffer
  )
{
  if (Buffer == NULL) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\BaseLib\\Unaligned.c",
      0xC0u,
      "Buffer != ((void *) 0)"
      );
  }
  return *Buffer;
}


//
// ---------------------------------------------------------------------------
// EfiAcquireLock ()
//
// Raise the task priority level (TPL) to Lock->Tpl and mark the lock
// as acquired. Asserts if the lock is already held or uninitialised.
// ---------------------------------------------------------------------------
//
VOID
EFIAPI
EfiAcquireLock (
  EFI_LOCK      *Lock
  )
{
  if (Lock == NULL) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\UefiLib\\UefiLib.c",
      0x1BEu,
      "Lock != ((void *) 0)"
      );
  }
  if (Lock->Lock != EfiLockReleased) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\UefiLib\\UefiLib.c",
      0x1BFu,
      "Lock->Lock == EfiLockReleased"
      );
  }

  Lock->OwnerTpl = gBS->RaiseTPL (Lock->Tpl);
  Lock->Lock     = EfiLockAcquired;
}


//
// ---------------------------------------------------------------------------
// EfiReleaseLock ()
//
// Restore the TPL to the saved OwnerTpl and mark the lock as released.
// Asserts if the lock was not acquired.
// ---------------------------------------------------------------------------
//
VOID
EFIAPI
EfiReleaseLock (
  EFI_LOCK      *Lock
  )
{
  EFI_TPL  OwnerTpl;

  if (Lock == NULL) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\UefiLib\\UefiLib.c",
      0x202u,
      "Lock != ((void *) 0)"
      );
  }
  if (Lock->Lock != EfiLockAcquired) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\UefiLib\\UefiLib.c",
      0x203u,
      "Lock->Lock == EfiLockAcquired"
      );
  }

  OwnerTpl           = Lock->OwnerTpl;
  Lock->Lock         = EfiLockReleased;
  gBS->RestoreTPL (OwnerTpl);
}


//
// ---------------------------------------------------------------------------
// InternalCopyMem ()
//
// Copy memory from source to destination, handling forward/backward copies
// to support overlapping buffers. Uses 8-byte qmemcpy where possible.
// ---------------------------------------------------------------------------
//
CHAR8 *
InternalCopyMem (
  CHAR8         *Destination,
  const CHAR8   *Source,
  UINTN         Length
  )
{
  CHAR8         *Dst;
  UINTN         LengthAligned;
  UINTN         LengthRemainder;

  if (Source < Destination && &Source[Length - 1] >= Destination) {
    //
    // Overlapping region: copy backward to avoid corruption.
    //
    Source = &Source[Length - 1];
    Dst    = &Destination[Length - 1];
  } else {
    //
    // No overlap: copy forward in 8-byte chunks + tail.
    //
    LengthAligned   = Length >> 3;
    LengthRemainder = Length & 7;

    qmemcpy (Destination, Source, 8 * LengthAligned);
    Source = &Source[8 * LengthAligned];
    Dst    = &Destination[8 * LengthAligned];
  }

  qmemcpy (Dst, Source, Length & 7);
  return Destination;
}


//
// ---------------------------------------------------------------------------
// InternalZeroMem ()
//
// Zero memory in 8-byte chunks with memset tail.
// ---------------------------------------------------------------------------
//
CHAR8 *
InternalZeroMem (
  CHAR8         *Buffer,
  UINTN         Length
  )
{
  UINTN  Aligned;

  Aligned = Length >> 3;
  memset (Buffer, 0, 8 * Aligned);
  memset (&Buffer[8 * Aligned], 0, Length & 7);

  return Buffer;
}


//
// ---------------------------------------------------------------------------
// DataHubFindDataRecordByFilter ()
//
// Walk the DataRecordList looking for the first record whose Flags match
// the supplied Filter and whose MonotonicCount matches *MonotonicOutput
// (or the first record if *MonotonicOutput is NULL on entry).
//
// On success, returns the DATA_HUB_RECORD* (as LIST_ENTRY*) and sets
// *MonotonicOutput to the next distinct MonotonicCount in the matching
// records.
// ---------------------------------------------------------------------------
//
LIST_ENTRY *
DataHubFindDataRecordByFilter (
  IN CONST LIST_ENTRY   *DataRecordListHead,
  IN UINT64             Filter,
  IN OUT LIST_ENTRY     **MonotonicOutput
  )
{
  LIST_ENTRY      *Node;
  DATA_HUB_RECORD *Record;
  LIST_ENTRY      *ReturnRecord;
  DATA_HUB_RECORD *Record_1;
  UINT64          SavedCount;

  //
  // Initialise SavedCount from the output parameter if present, else 0.
  //
  SavedCount = (*MonotonicOutput != NULL) ? (UINT64)*MonotonicOutput : 0;

  ReturnRecord = NULL;

  for (Node = GetFirstNode (DataRecordListHead);
       Node != (LIST_ENTRY *)DataRecordListHead;
       Node = GetNextNode (DataRecordListHead, Node))
  {
    Record = DATA_HUB_RECORD_FROM_LINK (Node);

    if ((Filter & Record->Flags) != 0 &&
        (Record->MonotonicCount == SavedCount || SavedCount == 0))
    {
      //
      // Record matches. Zero out the caller's output, then scan forward
      // for the next record with the same filter match to return the
      // next distinct MonotonicCount.
      //
      *MonotonicOutput = NULL;
      ReturnRecord     = (LIST_ENTRY *)Record;

      while (Node != (LIST_ENTRY *)DataRecordListHead) {
        Node = GetNextNode (DataRecordListHead, Node);
        Record_1 = DATA_HUB_RECORD_FROM_LINK (Node);

        if ((Filter & Record_1->Flags) != 0) {
          *MonotonicOutput = (LIST_ENTRY *)Record_1->MonotonicCount;
          return ReturnRecord;
        }
      }
      return ReturnRecord;
    }
  }

  return ReturnRecord;
}


//
// ---------------------------------------------------------------------------
// DataHubFindClassByGuid ()
//
// Scan the DataClassList for a node whose DataClassGuid matches the
// input GUID pointer (identity comparison, not GUID content).
// ---------------------------------------------------------------------------
//
DATA_HUB_CLASS *
DataHubFindClassByGuid (
  IN LIST_ENTRY     *ClassListHead,
  IN EFI_GUID       *DataClassGuid
  )
{
  LIST_ENTRY      *Node;
  DATA_HUB_CLASS  *Class;

  for (Node = GetFirstNode (ClassListHead);
       Node != ClassListHead;
       Node = GetNextNode (ClassListHead, Node))
  {
    Class = DATA_HUB_CLASS_FROM_LINK (Node);

    if (Class->DataClassGuid == DataClassGuid) {
      return Class;
    }
  }

  return NULL;
}


//
// ---------------------------------------------------------------------------
// DataHubLogData ()
//
// Core logging function. Allocates a DATA_HUB_RECORD + user data payload,
// populates it with the caller-supplied GUIDs and data, then appends it
// to the DataRecordList. After the record is published, registered class
// listeners whose filter matches the record's DataEntrySize are signalled.
//
// The record structure layout at offset:
//   +0x00: Signature   = DATA_HUB_RECORD_SIGNATURE
//   +0x08: LIST_ENTRY  (RecordListEntry)
//   +0x24: SelfPtr     (back pointer)
//   +0x28: Flags       = 0x4800C0
//   +0x2C: DataPayloadSize = (DataPayloadSize + 72)
//   +0x30: ProducerName (GUID)
//   +0x40: DataRecordGuid (GUID)
//   +0x50: DataSize    = DataEntrySize
//   +0x68: MonotonicCount (global incrementing serial)
//   +0x70: Variable-length user payload
// ---------------------------------------------------------------------------
//
EFI_STATUS
EFIAPI
DataHubLogData (
  IN EFI_DATA_HUB_PROTOCOL  *This,
  IN EFI_GUID               *DataRecordGuid,
  IN EFI_GUID               *ProducerName,
  IN UINT64                 DataEntrySize,
  IN VOID                   *Data,
  IN UINT32                 DataPayloadSize
  )
{
  DATA_HUB_PRIVATE_DATA  *Private;
  UINT8                  *RecordBuffer;
  DATA_HUB_RECORD        *Record;
  LIST_ENTRY             *Node;
  DATA_HUB_CLASS         *Class;

  Private = DATA_HUB_PRIVATE_DATA_FROM_PROTOCOL (This);

  //
  // Zero the local timestamp buffer (16 bytes).
  //
  ZeroMem (&RecordBuffer, 16);

  //
  // Sample the TPL for possible RTC read; if <= TPL_APPLICATION, read CMOS.
  // (This is the TPL check for the timestamp -- the original does not
  //  actually populate a proper timestamp, just a placeholder).
  //
  if (gBS->RaiseTPL (TPL_HIGH_LEVEL) <= 8) {
    //
    // TPL is low enough for RTC access -- restore and read timestamp.
    //
    gBS->RestoreTPL (TPL_APPLICATION);
    //
    // Placeholder: real EDK2 would call gRT->GetTime here, but this
    // binary does not seem to do so. The timestamp field is left zeroed.
    //
  } else {
    gBS->RestoreTPL (TPL_HIGH_LEVEL);
    //
    // TPL too high, cannot read RTC; timestamp stays zero.
    //
  }

  //
  // Acquire the data lock.
  //
  if (Private->DataLock.Lock == EfiLockUninitialized) {
    DebugAssert ("Lock->Lock != EfiLockUninitialized ....");
  }
  if (Private->DataLock.Lock == EfiLockAcquired) {
    return EFI_ACCESS_DENIED;
  }
  EfiAcquireLock (&Private->DataLock);

  //
  // Allocate the record: sizeof(DATA_HUB_RECORD) base (0x70) + payload.
  //
  RecordBuffer = AllocatePool (DataPayloadSize + 112);
  if (RecordBuffer == NULL) {
    EfiReleaseLock (&Private->DataLock);
    return EFI_OUT_OF_RESOURCES;
  }

  if (DataPayloadSize != (UINT32)-112) {
    ZeroMem (RecordBuffer, DataPayloadSize + 112);
  }

  Record = (DATA_HUB_RECORD *)RecordBuffer;

  //
  // Populate the record fields.
  //
  Record->Signature          = DATA_HUB_RECORD_SIGNATURE;
  Record->DataPayloadSize    = DataPayloadSize + 72;
  Record->Flags              = 0x4800C0;

  CopyMem (&Record->ProducerName,   ProducerName,  sizeof (EFI_GUID));
  CopyMem (&Record->DataRecordGuid, DataRecordGuid, sizeof (EFI_GUID));

  Record->DataSize       = DataEntrySize;
  Record->MonotonicCount = ++Private->TotalMonotonicCount;

  Record->SelfPtr = (DATA_HUB_RECORD *)(RecordBuffer + 40);

  //
  // Link into the DataRecordList head.
  //
  InsertTailList (&Private->DataRecordListHead, &Record->RecordListEntry);

  //
  // Copy the user data payload if non-empty.
  //
  if (DataPayloadSize > 0) {
    CopyMem (Record->DataPayload, Data, DataPayloadSize);
  }

  EfiReleaseLock (&Private->DataLock);

  //
  // Walk registered data class list and signal any listener whose
  // DataEntrySize mask matches, and either has a zeroed GUID storage
  // (matches all) or a CompareGuid match with the record's DataRecordGuid.
  //
  Node = GetFirstNode (&Private->DataClassListHead);
  while (Node != &Private->DataClassListHead) {
    Class = DATA_HUB_CLASS_FROM_LINK (Node);

    if ((DataEntrySize & Class->Filter) != 0) {
      if ((ReadUnaligned64 ((UINT64 *)&Class->DataClassGuidStorage) == 0 &&
           ReadUnaligned64 ((UINT64 *)&Class->DataClassGuidStorage + 1) == 0) ||
          CompareGuid (&Class->DataClassGuidStorage, DataRecordGuid))
      {
        gBS->SignalEvent ((EFI_EVENT)Class->DataClassGuid);
      }
    }

    Node = GetNextNode (
             &Private->DataClassListHead,
             &Class->ClassListEntry
             );
  }

  return EFI_SUCCESS;
}


//
// ---------------------------------------------------------------------------
// DataHubGetNextDataRecord ()
//
// Returns the next data record matching the optional MonotonicCount
// and/or class GUID filter. See EDK2 specification for
// EFI_DATA_HUB_PROTOCOL.GetNextDataRecord().
// ---------------------------------------------------------------------------
//
EFI_STATUS
EFIAPI
DataHubGetNextDataRecord (
  IN EFI_DATA_HUB_PROTOCOL    *This,
  IN OUT UINT64               *MonotonicCount OPTIONAL,
  IN OUT EFI_EVENT            **FilterEvent OPTIONAL,
  OUT EFI_DATA_RECORD         **Record
  )
{
  DATA_HUB_PRIVATE_DATA  *Private;
  LIST_ENTRY              *FoundRecord;
  UINT64                 ClassFilter;
  DATA_HUB_CLASS         *Class;

  Private = DATA_HUB_PRIVATE_DATA_FROM_PROTOCOL (This);

  if (FilterEvent == NULL) {
    //
    // No class filter -- iterate all records.
    //
    FoundRecord = DataHubFindDataRecordByFilter (
                    &Private->DataRecordListHead,
                    15,
                    (LIST_ENTRY **)MonotonicCount
                    );
    *Record = (EFI_DATA_RECORD *)FoundRecord;
    return (FoundRecord != NULL) ? EFI_SUCCESS : EFI_NOT_FOUND;
  }

  //
  // A class GUID was provided -- find its registration node.
  //
  Class = DataHubFindClassByGuid (&Private->DataClassListHead, (EFI_GUID *)*FilterEvent);
  if (Class == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  ClassFilter = Class->Filter;

  if (*MonotonicCount != 0 || (ClassFilter == 0)) {
    FoundRecord = DataHubFindDataRecordByFilter (
                    &Private->DataRecordListHead,
                    ClassFilter,
                    (LIST_ENTRY **)MonotonicCount
                    );
    *Record = (EFI_DATA_RECORD *)FoundRecord;
    if (FoundRecord == NULL) {
      return EFI_NOT_FOUND;
    }

    Class->DataClassGuid = (EFI_GUID *)*MonotonicCount;
    if (*MonotonicCount == 0) {
      *MonotonicCount = ((DATA_HUB_RECORD *)FoundRecord)->MonotonicCount;
    }
    return EFI_SUCCESS;
  } else {
    //
    // Resume from the saved MonotonicCount in the class node.
    //
    *MonotonicCount = (UINT64)Class->DataClassGuid;

    FoundRecord = DataHubFindDataRecordByFilter (
                    &Private->DataRecordListHead,
                    ClassFilter,
                    (LIST_ENTRY **)MonotonicCount
                    );
    *Record = (EFI_DATA_RECORD *)FoundRecord;
    if (FoundRecord == NULL) {
      return EFI_NOT_FOUND;
    }

    Class->DataClassGuid = (EFI_GUID *)*MonotonicCount;
  }

  return EFI_SUCCESS;
}


//
// ---------------------------------------------------------------------------
// DataHubRegisterDataClass ()
//
// Register a new data class for filtering. The class is identified by
// DataClassGuid and ProducerName; records whose DataEntrySize & Filter
// produce a non-zero result will signal the caller's event.
// ---------------------------------------------------------------------------
//
EFI_STATUS
EFIAPI
DataHubRegisterDataClass (
  IN EFI_DATA_HUB_PROTOCOL  *This,
  IN EFI_GUID               *DataClassGuid,
  IN EFI_GUID               *ProducerName,
  IN UINT64                 Filter,
  IN VOID                   *Data
  )
{
  DATA_HUB_PRIVATE_DATA  *Private;
  DATA_HUB_CLASS         *NewClass;

  Private = DATA_HUB_PRIVATE_DATA_FROM_PROTOCOL (This);

  //
  // Allocate a new DATA_HUB_CLASS node (0x48 bytes).
  //
  NewClass = AllocatePool (sizeof (DATA_HUB_CLASS));
  if (NewClass == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  NewClass = ZeroMem (NewClass, sizeof (DATA_HUB_CLASS));
  if (NewClass == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Populate the class node.
  //
  NewClass->Signature     = DATA_HUB_CLASS_SIGNATURE;
  NewClass->DataClassGuid = DataClassGuid;
  NewClass->ThisPtr       = (DATA_HUB_CLASS **)ProducerName;

  if (Filter == 0) {
    Filter = 15;
  }
  NewClass->Filter = Filter;

  if (Data != NULL) {
    CopyMem (&NewClass->DataClassGuidStorage, Data, sizeof (EFI_GUID));
  }

  //
  // Bail out if the class is already registered.
  //
  if (DataHubFindClassByGuid (&Private->DataClassListHead, DataClassGuid) != NULL) {
    FreePool (NewClass);
    return EFI_ALREADY_STARTED;
  }

  //
  // Insert into the class list under the lock.
  //
  EfiAcquireLock (&Private->DataLock);
  InsertTailList (&Private->DataClassListHead, &NewClass->ClassListEntry);
  EfiReleaseLock (&Private->DataLock);

  //
  // Signal the class event so that callers blocking on it are woken up.
  //
  gBS->SignalEvent ((EFI_EVENT)DataClassGuid);

  return EFI_SUCCESS;
}


//
// ---------------------------------------------------------------------------
// DataHubUnregisterDataClass ()
//
// Remove a registered data class by its GUID pointer.
// ---------------------------------------------------------------------------
//
EFI_STATUS
EFIAPI
DataHubUnregisterDataClass (
  IN EFI_DATA_HUB_PROTOCOL  *This,
  IN EFI_GUID               *DataClassGuid
  )
{
  DATA_HUB_PRIVATE_DATA  *Private;
  DATA_HUB_CLASS         *Class;

  Private = DATA_HUB_PRIVATE_DATA_FROM_PROTOCOL (This);

  //
  // Find the class node and remove it from the list.
  //
  Class = DataHubFindClassByGuid (&Private->DataClassListHead, DataClassGuid);
  if (Class == NULL) {
    return EFI_NOT_FOUND;
  }

  EfiAcquireLock (&Private->DataLock);
  RemoveEntryList (&Class->ClassListEntry);
  EfiReleaseLock (&Private->DataLock);

  return EFI_SUCCESS;
}


//
// ---------------------------------------------------------------------------
// GetHobList ()
//
// Locate the HOB list via gBS->LocateProtocol. Caches the result.
// ---------------------------------------------------------------------------
//
VOID *
GetHobList (
  VOID
  )
{
  if (mSwitchHobList == NULL) {
    //
    // Sample the TPL. Only continue if we are at a safe (low) TPL.
    //
    if (gBS->RaiseTPL (TPL_HIGH_LEVEL) <= TPL_APPLICATION) {
      gBS->RestoreTPL (TPL_APPLICATION);

      gBS->LocateProtocol (
             &gEfiDataHubProtocolGuid,
             NULL,
             (VOID **)&mSwitchHobList
             );
      return mSwitchHobList;
    }

    gBS->RestoreTPL (TPL_HIGH_LEVEL);
    return NULL;
  }
  return mSwitchHobList;
}


//
// ---------------------------------------------------------------------------
// DebugPrintWithCmosCheck ()
//
// Conditional debug print controlled by CMOS offset 0x4B (Boot/Diag
// flags register). Only prints if the boot mode matches the expected
// debug verbosity threshold.
// ---------------------------------------------------------------------------
//
CHAR8
EFIAPI
DebugPrintWithCmosCheck (
  IN UINTN       ErrorLevel,
  IN CONST CHAR8 *Format,
  ...
  )
{
  UINTN           HobList;
  UINT64          DebugMask;
  UINT8           CmosByte;
  CHAR8           BootMode;
  DEBUG_PROPERTY  *DebugProperty;
  VA_LIST         VaList;

  VA_START (VaList, Format);

  DebugProperty = GetHobList ();
  DebugMask     = 0;

  if (DebugProperty != NULL) {
    //
    // Access CMOS 0x70/0x71 register 0x4B (Boot/Diag flags).
    //
    CmosByte = __inbyte (0x70);
    __outbyte (0x70, CmosByte & 0x80 | 0x4B);
    BootMode = __inbyte (0x71);

    if ((UINT8)BootMode > 3) {
      if (BootMode == 0) {
        BootMode = (MEMORY[0xFDAF0490] & 2) | 1;
      }
    }

    if ((UINT8)(BootMode - 1) <= 0xFD) {
      DebugMask = (BootMode == 1)
                  ? EFI_D_INFO    // 0x80000004
                  : EFI_D_ERROR;  // 0x80000006
    }

    if ((DebugMask & ErrorLevel) != 0) {
      (*DebugProperty)(ErrorLevel, Format, VaList);
    }
  }

  VA_END (VaList);
  return (CHAR8)DebugProperty;
}


//
// ---------------------------------------------------------------------------
// DebugAssert ()
//
// Assertion handler obtained from the HOB-linked debug properties.
// ---------------------------------------------------------------------------
//
VOID
EFIAPI
DebugAssert (
  IN CONST CHAR8   *FileName,
  IN UINTN         LineNumber,
  IN CONST CHAR8   *Description
  )
{
  DEBUG_PROPERTY  *DebugProperty;

  DebugProperty = GetHobList ();
  if (DebugProperty != NULL) {
    //
    // Call the assert handler at field +8 in the HOB structure.
    ((DEBUG_ASSERT_HANDLER)DebugProperty[1]) (FileName, LineNumber, Description);
  }
}


//
// ---------------------------------------------------------------------------
// HobLibConstructor ()
//
// Find the gEfiHobListGuid entry in the System Table's configuration
// table array and cache the pointer.
// ---------------------------------------------------------------------------
//
EFI_STATUS
HobLibConstructor (
  VOID
  )
{
  EFI_GUID       gEfiHobListGuid;
  EFI_GUID       *HobGuid;
  UINTN          Index;
  VOID           *TableEntry;

  if (mHobList != NULL) {
    return EFI_SUCCESS;
  }

  //
  // gEfiHobListGuid = 7739F24C-93D7-11D4-9A3A-0090273FC14D
  //
  gEfiHobListGuid.Data1 = 0x7739F24C;
  gEfiHobListGuid.Data2 = 0x93D7;
  gEfiHobListGuid.Data3 = 0x11D4;
  gEfiHobListGuid.Data4[0] = 0x9A;
  gEfiHobListGuid.Data4[1] = 0x3A;
  gEfiHobListGuid.Data4[2] = 0x00;
  gEfiHobListGuid.Data4[3] = 0x90;
  gEfiHobListGuid.Data4[4] = 0x27;
  gEfiHobListGuid.Data4[5] = 0x3F;
  gEfiHobListGuid.Data4[6] = 0xC1;
  gEfiHobListGuid.Data4[7] = 0x4D;

  mHobList = NULL;

  if (gST->NumberOfTableEntries > 0) {
    for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {
      HobGuid = &gST->ConfigurationTable[Index].VendorGuid;

      if (CompareGuid (&gEfiHobListGuid, HobGuid)) {
        mHobList = (VOID *)gST->ConfigurationTable[Index].VendorTable;
        break;
      }
    }

    if (mHobList == NULL) {
      DebugPrintWithCmosCheck (
        EFI_D_ERROR,
        "\nASSERT_EFI_ERROR (Status = %r)\n",
        EFI_NOT_FOUND
        );
      DebugAssert (
        "e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
        0x36u,
        "!EFI_ERROR (Status)"
        );
    }
  } else {
    DebugPrintWithCmosCheck (
      EFI_D_ERROR,
      "\nASSERT_EFI_ERROR (Status = %r)\n",
      EFI_NOT_FOUND
      );
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
      0x36u,
      "!EFI_ERROR (Status)"
      );
  }

  if (mHobList == NULL) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
      0x37u,
      "mHobList != ((void *) 0)"
      );
  }

  return EFI_SUCCESS;
}


//
// ---------------------------------------------------------------------------
// DataHubDriverEntryPoint ()
//
// Initialise the private DATA_HUB_PRIVATE_DATA structure, set up the
// protocol function table, initialise the linked lists and lock, then
// install the EFI_DATA_HUB_PROTOCOL on a new UEFI handle.
// ---------------------------------------------------------------------------
//
EFI_STATUS
DataHubDriverEntryPoint (
  VOID
  )
{
  EFI_STATUS  Status;
  EFI_HANDLE  Handle;

  //
  // Initialise the private instance structure.
  //
  mPrivateData.Signature = DATA_HUB_PRIVATE_SIGNATURE;

  //
  // Hook up protocol function pointers.
  //
  mPrivateData.DataHubProtocol.LogData            = DataHubLogData;
  mPrivateData.DataHubProtocol.GetNextDataRecord  = DataHubGetNextDataRecord;
  mPrivateData.DataHubProtocol.RegisterDataClass  = DataHubRegisterDataClass;
  mPrivateData.DataHubProtocol.UnregisterDataClass= DataHubUnregisterDataClass;

  //
  // Initialise linked list heads to self-loop (empty list).
  //
  mPrivateData.DataRecordListHead.Flink = &mPrivateData.DataRecordListHead;
  mPrivateData.DataRecordListHead.Blink = &mPrivateData.DataRecordListHead;

  mPrivateData.DataClassListHead.Flink  = &mPrivateData.DataClassListHead;
  mPrivateData.DataClassListHead.Blink  = &mPrivateData.DataClassListHead;

  //
  // Initialise the lock.
  //
  mPrivateData.DataLock.Tpl  = TPL_NOTIFY;       // 16
  mPrivateData.DataLock.Lock = EfiLockReleased;   // 1

  //
  // Attempt LocateProtocol first (to see if this handle already exists),
  // then install the protocol on a new (or existing) handle.
  //
  Handle = NULL;

  Status = gBS->LocateProtocol (
                  &gEfiDataHubProtocolGuid,
                  NULL,
                  (VOID **)&Handle
                  );
  if (EFI_ERROR (Status)) {
    Handle = NULL;
  }

  Status = gBS->InstallProtocolInterface (
                  &Handle,
                  &gEfiDataHubProtocolGuid,
                  EFI_NATIVE_INTERFACE,
                  &mPrivateData.DataHubProtocol
                  );

  return Status;
}


//
// ---------------------------------------------------------------------------
// ModuleEntryPoint -- UEFI DXE driver entry point.
//
// Saves ImageHandle, SystemTable, BootServices, and RuntimeServices into
// the module's global copies, initialises the HOB list, then calls
// DataHubDriverEntryPoint() to install the protocol.
// ---------------------------------------------------------------------------
//
EFI_STATUS
EFIAPI
ModuleEntryPoint (
  IN EFI_HANDLE         ImageHandle,
  IN EFI_SYSTEM_TABLE   *SystemTable
  )
{
  gImageHandle    = ImageHandle;
  gST             = SystemTable;
  gBS             = SystemTable->BootServices;
  gRT             = SystemTable->RuntimeServices;

  //
  // Initialise HOB list pointer (needed by debug functions and
  // CMOS-aware debug print).
  //
  HobLibConstructor ();

  //
  // Install the Data Hub Protocol.
  //
  return DataHubDriverEntryPoint ();
}