/** @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 ();
}