Newer
Older
AMI-Aptio-BIOS-Reversed / CpPlatPkg / Uba / CfgDb / Pei / UbaConfigDatabasePei / UbaConfigDatabasePei.c
@Ajax Dong Ajax Dong 2 days ago 23 KB Full restructure
/** @file
  UBA Configuration Database PEIM

  Source: CpPlatPkg/Uba/CfgDb/Pei/CfgDbPei.c

  This PEIM implements a configuration database service for UBA (UEFI BIOS
  Abstraction) during the PEI phase. UBA producers register configuration
  records keyed by GUID. On notification (HOB notify), the database is
  serialized into a HOB for consumption by the DXE phase counterpart.

  Copyright (c) 2024, American Megatrends International LLC.
  SPDX-License-Identifier: BSD-2-Clause-Patent
**/

#include <PiPei.h>
#include <Library/DebugLib.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/PeiServicesLib.h>
#include <Library/PeiServicesTablePointerLib.h>
#include <Library/HobLib.h>
#include "UbaConfigDatabasePei.h"

/**
  Retrieves the PEI Services pointer from the IDT-based table.
**/
INTN
GetPeiServices (
  VOID
  )
{
  IA32_DESCRIPTOR  Idtr;
  UINTN            *PeiServices;

  AsmReadIdtr (&Idtr);
  PeiServices = *(UINTN **)(*(UINTN *)&Idtr.Base - 4);
  ASSERT (PeiServices != NULL);
  return (INTN)PeiServices;
}

/**
  Locate and get the Debug Service PPI.
**/
EFI_PEI_PPI_DESCRIPTOR *
EFIAPI
PeiServicesGetDebugLibPpi (
  VOID
  )
{
  EFI_STATUS            Status;
  INTN                  PeiServices;
  UINTN                 Instance;
  EFI_PEI_PPI_DESCRIPTOR *PpiDescriptor;
  VOID                  *Ppi;

  PeiServices = GetPeiServices ();
  Instance = 0;
  Status = ((EFI_PEI_SERVICES **)PeiServices)->LocatePpi (
             PeiServices,
             &gEfiDebugServicePpiGuid,
             Instance,
             &PpiDescriptor,
             &Ppi
             );
  if (!EFI_ERROR (Status)) {
    return (EFI_PEI_PPI_DESCRIPTOR *)Ppi;
  }
  return NULL;
}

/**
  Debug print via the Debug Service PPI.
**/
VOID
EFIAPI
PeiDebugPrint (
  IN UINTN  ErrorLevel,
  IN CONST CHAR8  *Format,
  ...
  )
{
  EFI_PEI_PPI_DESCRIPTOR *DebugPpi;
  UINTN                  Enabled;

  DebugPpi = PeiServicesGetDebugLibPpi ();
  if (DebugPpi != NULL) {
    Enabled = DebugPrintEnabled ();
    if ((Enabled & ErrorLevel) != 0) {
      VA_LIST  Va;
      VA_START (Va, Format);
      ((DEBUG_PRINT_PROTOCOL *)DebugPpi)->DebugPrint (ErrorLevel, Format, Va);
      VA_END (Va);
    }
  }
}

/**
  Debug assert via the Debug Service PPI.
**/
VOID
EFIAPI
PeiDebugAssert (
  IN CONST CHAR8  *FileName,
  IN UINTN        LineNumber,
  IN CONST CHAR8  *Description
  )
{
  EFI_PEI_PPI_DESCRIPTOR *DebugPpi;

  DebugPpi = PeiServicesGetDebugLibPpi ();
  if (DebugPpi != NULL) {
    ((DEBUG_ASSERT_PROTOCOL *)DebugPpi)->DebugAssert (FileName, LineNumber, Description);
  }
}

//
// Config Database helper functions
//

/**
  Get the signature field from a configuration database node.

  @param[in]  Node    Pointer to a UBA_CFG_DB_NODE (or its ListRecord field).
  @param[out] Count   Records count from the node.

  @retval EFI_SUCCESS  Signature validated and count retrieved.
**/
EFI_STATUS
EFIAPI
CfgDbLibGetNodeSignature (
  IN  VOID    *Node,
  OUT UINT32  *Count
  )
{
  UBA_CFG_DB_RECORD *Record;
  UBA_CFG_DB_NODE   *DbNode;

  Record = (UBA_CFG_DB_RECORD *)Node;
  if (Record->Signature == UBA_CFG_DB_SIGNATURE) {
    //
    // Node is a UBA_CFG_DB_RECORD; get count from container
    //
    DbNode = (UBA_CFG_DB_NODE *)((UINT8 *)Record - OFFSET_OF (UBA_CFG_DB_RECORD, ListEntry));
  } else {
    ASSERT (Record->Signature == UBA_CFG_DB_SIGNATURE);
    DbNode = (UBA_CFG_DB_NODE *)Record;
  }

  *Count = DbNode->RecordCount;
  return EFI_SUCCESS;
}

/**
  Allocate and initialize a new config database node.

  @param[in]  DbName          Name for this database.
  @param[in]  Type            Database type identifier.
  @param[in]  DbGuid          GUID key for this database.
  @param[in]  Description     Human-readable description.
  @param[out] NewNode         Pointer to the newly allocated node.

  @retval EFI_SUCCESS          Node created.
  @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
**/
EFI_STATUS
EFIAPI
CfgDbLibInitNode (
  IN  CHAR8               *DbName,
  IN  UINT32              Type,
  IN  GUID                *DbGuid,
  IN  CHAR8               *Description,
  OUT UBA_CFG_DB_NODE     **NewNode
  )
{
  UBA_CFG_DB_RECORD *Record;
  UBA_CFG_DB_NODE   *DbNode;
  UINT32            Signature;

  //
  // Check if DbName is actually a record; resolve to the container node
  //
  Record = (UBA_CFG_DB_RECORD *)DbName;
  if (Record->Signature == UBA_CFG_DB_SIGNATURE) {
    DbNode = (UBA_CFG_DB_NODE *)((UINT8 *)DbName - OFFSET_OF (UBA_CFG_DB_RECORD, ListEntry));
  } else {
    ASSERT (Record->Signature == UBA_CFG_DB_SIGNATURE);
    DbNode = (UBA_CFG_DB_NODE *)DbName;
  }

  DbNode = (UBA_CFG_DB_NODE *)PeiServicesAllocateZeroPool (sizeof (UBA_CFG_DB_NODE));
  if (DbNode == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  DbNode->Signature = UBA_CFG_DB_SIGNATURE;
  DbNode->Version   = 1;
  DbNode->RecordCount = Type;

  if (Description != NULL) {
    AsciiStrnCpyS (Description, ASCII_STR_N (sizeof (DbNode->Name)));
  }

  if (DbGuid != NULL) {
    CopyMem (DbNode->Name, DbGuid, sizeof (GUID));
  }

  InitializeListHead (&DbNode->RecordListHead);

  if (NewNode != NULL) {
    *NewNode = DbNode;
    //
    // Update the database pointer in the original node
    //
    ((UBA_CFG_DB_NODE *)((UINT8 *)DbName - OFFSET_OF (UBA_CFG_DB_RECORD, ListEntry)))->RecordCount = (UINT32)(UINTN)DbNode;
  }

  return EFI_SUCCESS;
}

//
// CR (Container Record) macros
//
#define CR_FROM_LIST_ENTRY(Record, Type, Field) \
  (Type *)((UINT8 *)(Record) - OFFSET_OF (Type, Field))

/**
  Adjust offsets within a config database after relocation.

  This function adjusts internal pointers (linked list forward/back links,
  record data pointers) when the database is relocated within PEI memory.

  @param[in]  NodeBase  Pointer to the database record's ListEntry field.
  @param[in]  Increase  TRUE if addresses increased, FALSE if decreased.
  @param[in]  Delta     Size of the relocation.
**/
EFI_STATUS
EFIAPI
CfgDbLibAdjustOffsets (
  IN  VOID    *NodeBase,
  IN  BOOLEAN Increase,
  IN  INTN    Delta
  )
{
  UBA_CFG_DB_RECORD *Record;
  LIST_ENTRY         *ListHead;
  LIST_ENTRY         *Entry;

  Record = (UBA_CFG_DB_RECORD *)NodeBase;
  if (Record->Signature == UBA_CFG_DB_SIGNATURE) {
    Record = Record;
  } else {
    ASSERT (Record->Signature == UBA_CFG_DB_SIGNATURE);
    Record = (UBA_CFG_DB_RECORD *)NodeBase;
  }

  //
  // Only adjust if the relocation hasn't been applied yet
  //
  if (Record->Type != (UINT32)(UINTN)NodeBase) {
    if (Record->Guid.Data1 != 0) {
      if (Increase) {
        Record->Guid.Data1 += (UINT32)Delta;
      } else {
        Record->Guid.Data1 -= (UINT32)Delta;
      }

      ListHead = (LIST_ENTRY *)(Record->Guid.Data1 + OFFSET_OF (LIST_ENTRY, Flink));
      if (Increase) {
        ListHead->Flink += Delta;
        ListHead->Blink += Delta;
      } else {
        ListHead->Flink -= Delta;
        ListHead->Blink -= Delta;
      }

      Entry = (LIST_ENTRY *)ListHead->Flink;
      while (Entry != ListHead) {
        if (Increase) {
          Entry->Flink += Delta;
          Entry->Blink += Delta;
        } else {
          Entry->Flink -= Delta;
          Entry->Blink -= Delta;
        }

        Record = CR_FROM_LIST_ENTRY (Entry, UBA_CFG_DB_RECORD, ListEntry);
        ASSERT (Record->Signature == UBA_CFG_DB_SIGNATURE);
        if (Increase) {
          Record->DataSize += (UINT32)Delta;
        } else {
          Record->DataSize -= (UINT32)Delta;
        }

        Entry = (LIST_ENTRY *)Entry->Flink;
      }
    }

    Record->Type = (UINT32)(UINTN)NodeBase;
  }

  return EFI_SUCCESS;
}

/**
  Fixup relocations for a config database.

  @param[in]  RecordBase  Pointer to a UBA_CFG_DB_RECORD (ListEntry field).
**/
EFI_STATUS
EFIAPI
CfgDbLibFixupRelocation (
  IN  VOID  *RecordBase
  )
{
  UBA_CFG_DB_RECORD *Record;
  UINTN             RelocDelta;

  Record = (UBA_CFG_DB_RECORD *)RecordBase;
  if (Record->Signature == UBA_CFG_DB_SIGNATURE) {
    Record = Record;
  } else {
    ASSERT (Record->Signature == UBA_CFG_DB_SIGNATURE);
    Record = (UBA_CFG_DB_RECORD *)RecordBase;
  }

  if (Record->Type != (UINT32)(UINTN)RecordBase) {
    if (RecordBase <= (VOID *)(UINTN)Record->Type) {
      RelocDelta = (UINTN)Record->Type - (UINTN)RecordBase;
      return CfgDbLibAdjustOffsets (RecordBase, TRUE, RelocDelta);
    } else {
      RelocDelta = (UINTN)RecordBase - (UINTN)Record->Type;
      return CfgDbLibAdjustOffsets (RecordBase, FALSE, RelocDelta);
    }
  }

  return EFI_SUCCESS;
}

/**
  Get the database pointer from a record.

  This function resolves the UBA_CFG_DB_NODE pointer from a UBA_CFG_DB_RECORD
  and returns it via the database handle.

  @param[in]   NodeBase   Pointer to a UBA_CFG_DB_RECORD's ListEntry.
  @param[out]  Database   Pointer to receive the UBA_CFG_DB_NODE.

  @retval EFI_SUCCESS      Database found.
  @retval Others           Error from LocatePpi.
**/
EFI_STATUS
EFIAPI
CfgDbPeiGetDatabase (
  IN  VOID              *NodeBase,
  OUT UBA_CFG_DB_NODE   **Database
  )
{
  return EFI_SUCCESS; // Not found as standalone - resolved via PPI pointer
}

/**
  Get record count and key info from the database.

  @param[in]   DbName     Database name.
  @param[out]  Count      Record count.
  @param[out]  DbGuid     Database GUID.
  @param[out]  DbDesc     Database description.

  @retval EFI_SUCCESS      Info retrieved.
**/
EFI_STATUS
EFIAPI
CfgDbLibGetRecord (
  IN  CHAR8   *DbName,
  OUT UINT32  *Count,
  OUT GUID    *DbGuid,
  OUT CHAR8   *DbDesc
  )
{
  EFI_STATUS         Status;
  UBA_CFG_DB_NODE    *DbNode;
  UBA_CFG_DB_RECORD  *Record;

  Record = (UBA_CFG_DB_RECORD *)DbName;
  CfgDbLibFixupRelocation (Record);

  Status = CfgDbPeiGetDatabase (DbName, &DbNode);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  if (Count != NULL) {
    *Count = DbNode->RecordCount;
  }

  if (DbDesc != NULL) {
    UINT32  Len;
    Len = AsciiStrLen (DbNode->Name);
    AsciiStrCpyS (DbDesc, Len + 1, DbNode->Name);
  }

  if (DbGuid != NULL) {
    CopyMem (DbGuid, &DbNode->RecordListHead.Flink, sizeof (GUID));
  }

  return EFI_SUCCESS;
}

/**
  Find a record in the database by GUID key.

  @param[in]   DbNode     Database node.
  @param[in]   Key        GUID key to search for.
  @param[out]  Data       Buffer to receive record data.
  @param[out]  DataSize   On input, max buffer size; on output, actual size.

  @retval EFI_SUCCESS        Record found.
  @retval EFI_NOT_FOUND      No matching record.
  @retval EFI_BUFFER_TOO_SMALL  Buffer too small for data.
  @retval EFI_INVALID_PARAMETER Null parameter.
**/
EFI_STATUS
EFIAPI
CfgDbLibFindRecord (
  IN     UBA_CFG_DB_NODE  *DbNode,
  IN     GUID             *Key,
  OUT    VOID             *Data,
  IN OUT UINT32           *DataSize
  )
{
  LIST_ENTRY         *ListHead;
  LIST_ENTRY         *Entry;
  UBA_CFG_DB_RECORD  *Record;

  if ((DbNode == NULL) || (Key == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  ListHead = &DbNode->RecordListHead;

  if (IsListEmpty (ListHead)) {
    return EFI_NOT_FOUND;
  }

  Entry = GetFirstNode (ListHead);
  while (!IsNull (ListHead, Entry)) {
    Record = CR_FROM_LIST_ENTRY (Entry, UBA_CFG_DB_RECORD, ListEntry);
    ASSERT (Record->Signature == UBA_CFG_DB_SIGNATURE);

    if (Record == NULL) {
      ASSERT (Record != NULL);
      return EFI_NOT_FOUND;
    }

    if (CompareGuid (Key, &Record->Guid)) {
      if (DataSize != NULL) {
        if (*DataSize < Record->DataSize) {
          *DataSize = Record->DataSize;
          return EFI_BUFFER_TOO_SMALL;
        }
        *DataSize = Record->DataSize;
        if (Data != NULL && Record->DataSize > 0) {
          CopyMem (Data, Record->Data, Record->DataSize);
        }
        return EFI_SUCCESS;
      }
      return EFI_SUCCESS;
    }

    Entry = GetNextNode (ListHead, Entry);
  }

  return EFI_NOT_FOUND;
}

/**
  Add a record to the config database.

  @param[in]  DbBase    Pointer to a config database record's ListEntry.
  @param[in]  Src       Record data.
  @param[in]  DataSize  Size of record data.
  @param[in]  Key       GUID key.

  @retval EFI_SUCCESS            Record added.
  @retval EFI_OUT_OF_RESOURCES   Memory allocation failed.
  @retval EFI_INVALID_PARAMETER  Bad parameter.
**/
EFI_STATUS
EFIAPI
CfgDbLibAddRecord (
  IN  VOID    *DbBase,
  IN  VOID    *Src,
  IN  UINT32  DataSize,
  IN  GUID    *Key
  )
{
  UBA_CFG_DB_RECORD  *Record;
  UBA_CFG_DB_NODE    *DbNode;
  UBA_CFG_DB_RECORD  *NewRecord;
  UINT32             *NotifyPpi;

  if ((Src == NULL) || (DataSize == 0) || (Key == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  Record = (UBA_CFG_DB_RECORD *)DbBase;
  if (Record->Signature == UBA_CFG_DB_SIGNATURE) {
    Record = Record;
  } else {
    ASSERT (Record->Signature == UBA_CFG_DB_SIGNATURE);
    Record = (UBA_CFG_DB_RECORD *)DbBase;
  }

  Status = CfgDbPeiGetDatabase (DbBase, &DbNode);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  NewRecord = (UBA_CFG_DB_RECORD *)PeiServicesAllocateZeroPool (sizeof (UBA_CFG_DB_RECORD));
  if (NewRecord == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  NewRecord->Signature = UBA_CFG_DB_SIGNATURE;
  NewRecord->Version   = 1;
  NewRecord->Type      = Record->Type;

  NewRecord->DataSize = DataSize;

  //
  // Allocate copy pool for the data
  //
  NewRecord->Data = PeiServicesAllocateCopyPool (DataSize, Src);
  if (NewRecord->Data == NULL) {
    // DataSize field still set but data=0 - proceed
  }

  //
  // Copy the GUID key
  //
  CopyMem (&NewRecord->Guid, Src, sizeof (GUID));
  CopyMem (&NewRecord->Guid, Key, sizeof (GUID));

  //
  // Insert into linked list
  //
  InsertTailList (&DbNode->RecordListHead, &NewRecord->ListEntry);
  DbNode->RecordCount++;
  DbNode->TotalRecords++;
  Record->Type++; // Increment type counter

  //
  // Install notification PPI for the added record
  //
  {
    UINT32 *NotifyDescriptor;
    NotifyDescriptor = (UINT32 *)PeiServicesAllocateZeroPool (0x0C);
    if (NotifyDescriptor != NULL) {
      NotifyDescriptor[0] = (UINT32)EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK;
      NotifyDescriptor[1] = (UINT32)(UINTN)NewRecord;
      NotifyDescriptor[2] = (UINT32)(UINTN)&Record->ListEntry.Flink;

      Status = ((EFI_PEI_SERVICES **)GetPeiServices ())->NotifyPpi (
                 GetPeiServices (),
                 (EFI_PEI_NOTIFY_DESCRIPTOR *)NotifyDescriptor
                 );
      ASSERT_EFI_ERROR (Status);
    } else {
      return EFI_OUT_OF_RESOURCES;
    }
  }

  return EFI_SUCCESS;
}

/**
  Get data from the database by GUID key.

  @param[in]   DbName    Database name.
  @param[in]   Key       GUID key.
  @param[out]  Data      Buffer for record data.
  @param[in,out] DataSize  Buffer size / actual data size.

  @retval EFI_SUCCESS      Data retrieved.
**/
EFI_STATUS
EFIAPI
CfgDbLibGetData (
  IN     CHAR8   *DbName,
  IN     GUID    *Key,
  OUT    VOID    *Data,
  IN OUT UINT32  *DataSize
  )
{
  EFI_STATUS         Status;
  UBA_CFG_DB_NODE    *DbNode;
  UBA_CFG_DB_RECORD  *Record;

  if ((Key == NULL) || (Data == NULL) || (DataSize == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  Record = (UBA_CFG_DB_RECORD *)DbName;
  CfgDbLibFixupRelocation (Record);

  //
  // Validate signature
  //
  if (Record->Signature != UBA_CFG_DB_SIGNATURE) {
    ASSERT (Record->Signature == UBA_CFG_DB_SIGNATURE);
  }

  Status = CfgDbPeiGetDatabase (DbName, &DbNode);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  if (CfgDbLibFindRecord (DbNode, Key, Data, DataSize) != EFI_SUCCESS) {
    return EFI_NOT_FOUND;
  }

  return EFI_SUCCESS;
}

/**
  Build a GUID HOB containing the serialized configuration database.

  @param[in]  DbNode       Database node to serialize.
  @param[in]  HobGuid      GUID for the HOB.

  @retval EFI_SUCCESS      HOB built.
  @retval others           Error from HOB creation.
**/
EFI_STATUS
EFIAPI
CfgDbLibBuildHob (
  IN  UBA_CFG_DB_NODE  *DbNode,
  IN  GUID             *HobGuid
  )
{
  UINT32              HobSize;
  UBA_CFG_DB_HOB      *HobData;
  UBA_CFG_DB_HOB_RECORD *HobRecord;
  LIST_ENTRY          *ListHead;
  LIST_ENTRY          *Entry;
  UBA_CFG_DB_RECORD   *DbRecord;
  UINT32              Index;
  UINT8               *DataPtr;
  UINT32              NameLen;

  if (DbNode->RecordCount == 0) {
    return EFI_SUCCESS;
  }

  //
  // Calculate total HOB size:
  //  - Fixed header: sizeof(UBA_CFG_DB_HOB) = 68 bytes
  //  - Record array: RecordCount * 36 bytes per record descriptor
  //  - Data payload: sum of each record's DataSize
  //
  HobSize = sizeof (UBA_CFG_DB_HOB) + DbNode->RecordCount * sizeof (UBA_CFG_DB_HOB_RECORD);

  ListHead = &DbNode->RecordListHead;

  for (Entry = GetFirstNode (ListHead);
       !IsNull (ListHead, Entry);
       Entry = GetNextNode (ListHead, Entry))
  {
    DbRecord = CR_FROM_LIST_ENTRY (Entry, UBA_CFG_DB_RECORD, ListEntry);
    ASSERT (DbRecord->Signature == UBA_CFG_DB_SIGNATURE);
    HobSize += DbRecord->DataSize;
  }

  //
  // Create the HOB
  //
  HobData = (UBA_CFG_DB_HOB *)BuildGuidHob (HobGuid, HobSize);
  if (HobData == NULL) {
    ASSERT (HobData != NULL);
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Fill HOB header
  //
  HobData->Signature   = 0x48424355;  // 'UCBH'
  HobData->Version     = 1;
  CopyGuid (&HobData->DbGuid, HobGuid);
  HobData->HobDataSize = HobSize;
  HobData->DatabaseId  = DbNode->RecordCount;
  CopyGuid (&HobData->SourceDbGuid, (GUID *)&DbNode->RecordListHead.Flink);
  NameLen = AsciiStrLen (DbNode->Name) + 1;
  if (NameLen > 0) {
    CopyMem (HobData->SourceDbName, DbNode->Name, NameLen);
  }
  HobData->RecordCount = DbNode->RecordCount;

  //
  // Fill record array and data payload
  //
  HobRecord = (UBA_CFG_DB_HOB_RECORD *)((UINT8 *)HobData + sizeof (UBA_CFG_DB_HOB));
  DataPtr = (UINT8 *)HobRecord + DbNode->RecordCount * sizeof (UBA_CFG_DB_HOB_RECORD);

  Index = 0;
  for (Entry = GetFirstNode (ListHead);
       !IsNull (ListHead, Entry);
       Entry = GetNextNode (ListHead, Entry))
  {
    DbRecord = CR_FROM_LIST_ENTRY (Entry, UBA_CFG_DB_RECORD, ListEntry);
    ASSERT (DbRecord->Signature == UBA_CFG_DB_SIGNATURE);
    if (DbRecord == NULL) {
      ASSERT (DbRecord != NULL);
      return EFI_NOT_FOUND;
    }

    HobRecord->DataSize  = DbRecord->DataSize;
    HobRecord->Type      = DbRecord->Type;
    CopyGuid (&HobRecord->Key, &DbRecord->Guid);

    if (DbRecord->DataSize > 0) {
      CopyMem (DataPtr, DbRecord->Data, DbRecord->DataSize);
    }

    HobRecord->DataOffset = (UINT32)(UINTN)(DataPtr - (UINT8 *)HobData);
    HobRecord->Reserved   = 0;

    DataPtr += DbRecord->DataSize;
    HobRecord++;
    Index++;
  }

  return EFI_SUCCESS;
}

/**
  HOB notification callback: serializes the configuration database into a HOB
  when the notification PPI is signaled.

  @retval EFI_SUCCESS      HOB serialized successfully.
**/
EFI_STATUS
EFIAPI
CfgDbHobNotify (
  VOID
  )
{
  EFI_STATUS         Status;
  UBA_CFG_DB_NODE    *DbNode;
  UBA_CFG_DB_RECORD  *Record;
  GUID               HobGuid = { 0x249DDB57, 0x0D8E, 0x4B8B, { 0x87, 0xA0, 0xD0, 0x86, 0x26, 0x23, 0x46, 0x83 } };
  INTN               PeiServices;
  VOID               *Ppi;

  PeiServices = GetPeiServices ();
  Status = ((EFI_PEI_SERVICES **)PeiServices)->LocatePpi (
             PeiServices,
             &gUbaConfigDatabasePpiGuid,
             0,
             NULL,
             &Ppi
             );

  if (EFI_ERROR (Status)) {
    ASSERT_EFI_ERROR (Status);
    return Status;
  }

  //
  // Get the database node
  //
  Record = (UBA_CFG_DB_RECORD *)((UBA_CFG_DB_PPI *)Ppi)->Database;
  Status = CfgDbLibBuildHob (DbNode, &HobGuid);
  if (EFI_ERROR (Status)) {
    ASSERT_EFI_ERROR (Status);
  }

  return Status;
}

/**
  Create a new database (vtable entry for PPI protocol).

  @param[in]  DbName       Database name.
  @param[in]  Type         Database type.
  @param[in]  DbGuid       Database GUID.
  @param[in]  Description  Database description.

  @retval EFI_SUCCESS    Database created.
**/
EFI_STATUS
EFIAPI
CfgDbNotifyCreateDb (
  IN  CHAR8  *DbName,
  IN  UINT32 Type,
  IN  GUID   *DbGuid,
  IN  CHAR8  *Description
  )
{
  return CfgDbLibInitNodeAndRelocate (DbName, Type, (CHAR8 *)DbGuid, Description);
}

/**
  Init a node and fixup its relocations.

  @param[in]  DbName       Database name.
  @param[in]  Type         Database type.
  @param[in]  DbGuid       Database GUID.
  @param[in]  Description  Database description.

  @retval EFI_SUCCESS    Node initialized.
**/
EFI_STATUS
EFIAPI
CfgDbLibInitNodeAndRelocate (
  IN  CHAR8  *DbName,
  IN  UINT32 Type,
  IN  CHAR8  *DbGuid,
  IN  CHAR8  *Description
  )
{
  UBA_CFG_DB_NODE  *NewNode;

  NewNode = NULL;
  CfgDbLibFixupRelocation (DbName);
  return CfgDbLibInitNode (DbName, Type, (GUID *)DbGuid, Description, &NewNode);
}

//
// Notify callbacks for the PPI protocol
//
EFI_STATUS
EFIAPI
CfgDbNotifyGetInfo (
  IN  CHAR8   *DbName,
  OUT UINT32  *RecordCount,
  OUT GUID    *DbGuid,
  OUT CHAR8   *DbDescription
  )
{
  return CfgDbLibGetRecord (DbName, RecordCount, DbGuid, DbDescription);
}

EFI_STATUS
EFIAPI
CfgDbNotifyAddRecord (
  IN  CHAR8   *DbName,
  IN  GUID    *Key,
  IN  VOID    *Data,
  IN  UINT32  DataSize
  )
{
  return CfgDbLibAddRecord (DbName, (CHAR8 *)Data, DataSize, Key);
}

EFI_STATUS
EFIAPI
CfgDbNotifyFindKey (
  IN  CHAR8   *DbName,
  IN  GUID    *Key,
  OUT VOID    *Data,
  IN  OUT UINT32 *DataSize
  )
{
  return CfgDbLibGetData (DbName, (INTN)Key, (CHAR8 *)Data, DataSize);
}

//
// Module entry point
//

/**
  Main entry for the UBA Configuration Database PEIM.

  Allocates a database node, initializes the PPI protocol interface with
  callback functions, installs the PPI and a notification PPI (for HOB
  serialization), then returns.

  @retval EFI_SUCCESS           PPI installed successfully.
  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
**/
EFI_STATUS
EFIAPI
CfgDbPeimEntry (
  VOID
  )
{
  EFI_STATUS              Status;
  UBA_CFG_DB_PPI          *CfgDbPpi;
  UINT32                  *NotifyDescriptor;
  INTN                    PeiServices;
  EFI_PEI_PPI_DESCRIPTOR  *PpiDescriptor;

  //
  // Allocate the database node (0x3C = sizeof(UBA_CFG_DB_NODE))
  //
  CfgDbPpi = (UBA_CFG_DB_PPI *)PeiServicesAllocateZeroPool (sizeof (UBA_CFG_DB_NODE));
  if (CfgDbPpi == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  DEBUG ((DEBUG_INFO, "UbaConfigDatabasePeimEntry!\n"));

  //
  // Initialize the PPI structure
  //
  CfgDbPpi->Reserved       = UBA_CFG_DB_SIGNATURE;  // Signature at offset 0-3
  CfgDbPpi->Database       = 1;                      // Version
  // Database pointer filled by CfgDbLibInitNodeAndRelocate
  // Function table:
  // [2] = Internal list links (head - self-referential)
  //
  // PPI descriptor for installation
  //
  NotifyDescriptor = (UINT32 *)PeiServicesAllocateZeroPool (sizeof (EFI_PEI_PPI_DESCRIPTOR));
  if (NotifyDescriptor == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  NotifyDescriptor[0] = (UINT32)(UINTN)&CfgDbPpi->Reserved; // Not used
  NotifyDescriptor[1] = (UINT32)(UINTN)&gUbaConfigDatabasePpiGuid;
  NotifyDescriptor[2] = (UINT32)(UINTN)CfgDbPpi;

  //
  // Install the PPI
  //
  PeiServices = GetPeiServices ();
  Status = ((EFI_PEI_SERVICES **)PeiServices)->InstallPpi (
             PeiServices,
             NotifyDescriptor
             );

  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Install notification PPI for HOB serialization
  //
  Status = ((EFI_PEI_SERVICES **)GetPeiServices ())->NotifyPpi (
             GetPeiServices (),
             (EFI_PEI_NOTIFY_DESCRIPTOR *)&mUbaConfigDatabaseNotifyDescriptor
             );
  ASSERT_EFI_ERROR (Status);

  return Status;
}

/**
  Module entry point (thunk).

  @param[in]  ImageHandle   Image handle.
  @param[in]  SystemTable   System table.

  @retval EFI_SUCCESS       Entry executed.
**/
EFI_STATUS
EFIAPI
ModuleEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  return CfgDbPeimEntry ();
}