Newer
Older
AMI-Aptio-BIOS-Reversed / WatchdogTimer / WatchdogTimer.c
@Ajax Dong Ajax Dong 2 days ago 22 KB Init
/** @file
  WatchdogTimer.c -- Implementation of the Watchdog Timer UEFI Driver

  This module implements the EFI_WATCHDOG_TIMER_ARCH_PROTOCOL for the
  HR650X platform. The watchdog timer is a hardware feature that resets
  the system if firmware or software becomes unresponsive within a
  configurable time period.

  The driver performs the following:
  - Installs the EFI_WATCHDOG_TIMER_ARCH_PROTOCOL with RegisterHandler,
    SetTimerPeriod, and GetTimerPeriod services.
  - On timer expiry, calls the registered notification handler, then
    resets the system via gRT->ResetSystem().
  - Coordinates with the SMM watchdog handler via the SMM Communication
    Protocol to synchronize watchdog state between DXE and SMM phases.

  Source: HR650X BIOS WatchdogTimer.efi (Index 0083)
  Module: MdeModulePkg/Universal/WatchdogTimerDxe

  Copyright (C) 2025 Your Company. All rights reserved.
**/

#include "WatchdogTimer.h"

//
// ============================================================================
// Protocol GUIDs
// ============================================================================
//

///
/// EFI_WATCHDOG_TIMER_ARCH_PROTOCOL_GUID
/// {665E3FF5-46CC-11D4-9A38-0090273FC14D}
///
EFI_GUID  gEfiWatchdogTimerArchProtocolGuid =
  { 0x665E3FF5, 0x46CC, 0x11D4,
    { 0x9A, 0x38, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D } };

///
/// gEfiHobListGuid
/// {7739F24C-93D7-11D4-9A3A-0090273FC14D}
///
EFI_GUID  gEfiHobListGuid =
  { 0x7739F24C, 0x93D7, 0x11D4,
    { 0x9A, 0x3A, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D } };

///
/// gEfiSmmCommunicationProtocolGuid
/// {D2B2B828-0826-48A7-B3DF-983C006024F0}
///
EFI_GUID  gEfiSmmCommunicationProtocolGuid =
  { 0xD2B2B828, 0x0826, 0x48A7,
    { 0xB3, 0xDF, 0x98, 0x3C, 0x00, 0x60, 0x24, 0xF0 } };

///
/// gEfiGenericMemTestPatternGuid (HOB GUID for debug output device)
/// {36232936-0E76-31C8-A13A-3AF2FC1C3932}
///
EFI_GUID  gEfiGenericMemTestPatternGuid =
  { 0x36232936, 0x0E76, 0x31C8,
    { 0xA1, 0x3A, 0x3A, 0xF2, 0xFC, 0x1C, 0x39, 0x32 } };

//
// ============================================================================
// Global Variables
// ============================================================================
//

///
/// The EFI_WATCHDOG_TIMER_ARCH_PROTOCOL instance installed on the handle.
/// Contains function pointers for RegisterHandler and SetTimerPeriod.
/// The third field (GetTimerPeriod) is overlapped with mWatchdogTimerPeriod.
///
EFI_WATCHDOG_TIMER_ARCH_PROTOCOL  mWatchdogTimerProtocol = {
  WatchdogTimerRegisterHandler,
  WatchdogTimerSetTimerPeriod
  // GetTimerPeriod is implicitly at &mWatchdogTimerPeriod
};

///
/// Stores the current watchdog timer period in 100ns units.
/// Initialized to WATCHDOG_TIMER_DEFAULT_PERIOD (1500 = 150us).
/// This also aliases with the third entry of the protocol structure,
/// allowing GetTimerPeriod to return the period value directly.
///
UINT64  mWatchdogTimerPeriod = WATCHDOG_TIMER_DEFAULT_PERIOD;

///
/// Pointer to the registered watchdog notification function, or NULL.
///
VOID  *mWatchdogNotifyFunction = NULL;

///
/// Flag indicating whether a watchdog notification handler is registered.
///
VOID  *mWatchdogNotifyRegistered = NULL;

///
/// Cached pointer to the HOB list, obtained from the system configuration table.
///
VOID  *mHobList = NULL;

///
/// Cached pointer to the SMM Communication Protocol interface.
///
VOID  *mSmmCommunicationProtocol = NULL;

///
/// NMI flag byte. Accessed via CMOS I/O ports 0x70/0x71.
/// Bit layout: bit 7 of CMOS index 0x4B stores an NMI status value.
///
UINT8  mNmiFlag = 0;

//
// ============================================================================
// Function Implementations
// ============================================================================
//

/**
  Entry point for the Watchdog Timer DXE driver.

  This function initializes the driver globals (ImageHandle, SystemTable,
  BootServices, RuntimeServices), retrieves the HOB list, and installs
  the EFI_WATCHDOG_TIMER_ARCH_PROTOCOL onto the image handle.

  If the protocol is already installed, an ASSERT is generated.
  Memory for the driver's protocol instance is allocated and installed
  via InstallMultipleProtocolInterfaces().

  @param[in] ImageHandle  The firmware allocated handle for the EFI image.
  @param[in] SystemTable  A pointer to the EFI System Table.

  @return EFI_SUCCESS           The watchdog timer protocol was installed.
  @return EFI_ALREADY_STARTED   The protocol is already installed in the database.
  @return others                Error from AllocatePool or InstallMultipleProtocolInterfaces.
**/
EFI_STATUS
EFIAPI
WatchdogTimerDriverEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE *SystemTable
  )
{
  EFI_STATUS                    Status;
  EFI_WATCHDOG_TIMER_ARCH_PROTOCOL  *Protocol;

  //
  // Initialize UEFI global variables (gImageHandle, gST, gBS, gRT)
  //
  gImageHandle = ImageHandle;
  if (gImageHandle == NULL) {
    DebugAssert (
      __FILE__,
      __LINE__,
      "gImageHandle != ((void *) 0)"
      );
  }

  gST = SystemTable;
  if (gST == NULL) {
    DebugAssert (
      __FILE__,
      __LINE__,
      "gST != ((void *) 0)"
      );
  }

  gBS = SystemTable->BootServices;
  if (gBS == NULL) {
    DebugAssert (
      __FILE__,
      __LINE__,
      "gBS != ((void *) 0)"
      );
  }

  gRT = SystemTable->RuntimeServices;
  if (gRT == NULL) {
    DebugAssert (
      __FILE__,
      __LINE__,
      "gRT != ((void *) 0)"
      );
  }

  //
  // Initialize the HOB list
  //
  GetHobList ();

  //
  // Install the EFI_WATCHDOG_TIMER_ARCH_PROTOCOL
  //
  Status = gBS->LocateProtocol (
                  &gEfiWatchdogTimerArchProtocolGuid,
                  NULL,
                  (VOID **)&Protocol
                  );
  if (!EFI_ERROR (Status)) {
    //
    // Protocol already installed -- this is unexpected
    //
    DebugAssert (
      __FILE__,
      __LINE__,
      "&gEfiWatchdogTimerArchProtocolGuid already installed in database"
      );
    return Status;
  }

  //
  // Allocate pool for the protocol interface structure
  // EfiBootServicesData type, 16 bytes (2 function pointers)
  //
  Status = gBS->AllocatePool (
                  EfiBootServicesData,
                  sizeof (EFI_WATCHDOG_TIMER_ARCH_PROTOCOL),
                  (VOID **)&Protocol
                  );
  if (EFI_ERROR (Status)) {
    DebugAssertPrint (
      0x80000000,
      "\nASSERT_EFI_ERROR (Status = %r)\n",
      Status
      );
    DebugAssert (
      __FILE__,
      __LINE__,
      "!EFI_ERROR (Status)"
      );
    return Status;
  }

  //
  // Initialize the protocol structure
  //
  Protocol->RegisterHandler = WatchdogTimerRegisterHandler;
  Protocol->SetTimerPeriod  = WatchdogTimerSetTimerPeriod;
  //
  // Note: GetTimerPeriod is implicitly available as mWatchdogTimerPeriod
  // is aliased at the same offset as the third protocol function pointer.
  //

  //
  // Install the protocol on the image handle
  //
  Status = gBS->InstallMultipleProtocolInterfaces (
                  &ImageHandle,
                  &gEfiWatchdogTimerArchProtocolGuid,
                  Protocol,
                  NULL
                  );
  if (EFI_ERROR (Status)) {
    DebugAssertPrint (
      0x80000000,
      "\nASSERT_EFI_ERROR (Status = %r)\n",
      Status
      );
    DebugAssert (
      __FILE__,
      __LINE__,
      "!EFI_ERROR (Status)"
      );
    return Status;
  }

  return EFI_SUCCESS;
}

/**
  Notification handler called when the watchdog timer fires.

  This handler is invoked by the timer hardware when the watchdog
  period expires. It performs the following actions:
  1. Calls the SMM communication handler to synchronize state.
  2. Invokes any registered notification callback.
  3. Logs the system reset event via debug output.
  4. Resets the system via gRT->ResetSystem() with
     EfiResetCold and the watchdog reset reason.

  @param[in] TimePeriod     The current timer period in 100ns units.
  @param[in] WatchdogCode   The watchdog code data.
  @param[in] DataSize       Size of the watchdog data buffer.
  @param[in] WatchdogData   Pointer to the watchdog data buffer.
**/
VOID
EFIAPI
WatchdogTimerNotifyHandler (
  IN UINTN  TimePeriod,
  IN UINTN  WatchdogCode,
  IN UINTN  DataSize,
  IN VOID  *WatchdogData
  )
{
  //
  // Send SMM communication to update SMM watchdog handler state
  //
  SmmCommunicationSend ();

  //
  // Invoke registered notify function if present
  //
  if (mWatchdogNotifyRegistered != NULL) {
    ((EFI_WATCHDOG_TIMER_NOTIFY)mWatchdogNotifyRegistered) (
      TimePeriod,
      WatchdogCode,
      DataSize,
      WatchdogData
      );
  }

  //
  // Log the reset event
  //
  DebugAssertPrint (
    0x80000000,
    "Watchdog Timer reseting system\n"
    );

  //
  // Reset the system
  //
  gRT->ResetSystem (
         EfiResetCold,
         EFI_HARDWARE_ERROR,
         0,
         NULL
         );
}

/**
  Registers or unregisters a notification handler for the watchdog timer.

  If NotifyFunction is non-NULL and no handler is currently registered,
  the function is registered. If NotifyFunction is NULL and a handler
  is registered, it is unregistered. Attempting to register a handler
  when one already exists returns EFI_ALREADY_STARTED.

  @param[in] This             Pointer to the EFI_WATCHDOG_TIMER_ARCH_PROTOCOL instance.
  @param[in] NotifyFunction   Pointer to the notification function to register,
                              or NULL to unregister the current handler.

  @return EFI_SUCCESS           The handler was registered or unregistered.
  @return EFI_ALREADY_STARTED   A handler is already registered and NotifyFunction is non-NULL.
  @return EFI_INVALID_PARAMETER NotifyFunction is NULL but no handler is registered.
**/
EFI_STATUS
EFIAPI
WatchdogTimerRegisterHandler (
  IN EFI_WATCHDOG_TIMER_ARCH_PROTOCOL  *This,
  IN EFI_WATCHDOG_TIMER_NOTIFY          NotifyFunction
  )
{
  //
  // Register: caller wants to set a notification handler
  //
  if (NotifyFunction != NULL) {
    //
    // Check if handler is already registered
    //
    if (mWatchdogNotifyRegistered != NULL) {
      return EFI_ALREADY_STARTED;
    }
  } else {
    //
    // Unregister: caller wants to clear the handler
    //
    if (mWatchdogNotifyRegistered == NULL) {
      return EFI_UNSUPPORTED;
    }
  }

  //
  // Store/clear the notify function pointer
  //
  mWatchdogNotifyRegistered = (VOID *)NotifyFunction;

  return EFI_SUCCESS;
}

/**
  Sets the watchdog timer period.

  Sets the internal timer period storage and programs the hardware
  timer via gBS->SetTimer(). A period of zero disables the watchdog
  timer; any non-zero value arms the timer with a periodic signal.

  In the periodic case, the timer is set with the EFI_TIMER_PERIODIC
  type (value 2).

  @param[in] This           Pointer to the EFI_WATCHDOG_TIMER_ARCH_PROTOCOL instance.
  @param[in] TimerPeriod    The timer period in 100ns units.
                            Zero disables the watchdog timer.

  @return EFI_SUCCESS       The timer period was set and the hardware was programmed.
**/
EFI_STATUS
EFIAPI
WatchdogTimerSetTimerPeriod (
  IN EFI_WATCHDOG_TIMER_ARCH_PROTOCOL  *This,
  IN UINT64                             TimerPeriod
  )
{
  //
  // Store the period value (aliased with the GetTimerPeriod function pointer)
  //
  mWatchdogTimerPeriod = TimerPeriod;

  //
  // Program the hardware timer
  // - If TimerPeriod is non-zero, use periodic timer type (TimerPeriodic = 2)
  // - If TimerPeriod is zero, use cancel timer type (TimerCancel = 0)
  //
  return gBS->SetTimer (
                mWatchdogTimerPeriod,       // Note: this references the global, not the period
                (TimerPeriod != 0) ? TimerPeriodic : TimerCancel,
                TimerPeriod
                );
}

/**
  Retrieves the current watchdog timer period.

  Reads the internally stored timer period value and returns it to
  the caller.

  @param[in]  This           Pointer to the EFI_WATCHDOG_TIMER_ARCH_PROTOCOL instance.
  @param[out] TimerPeriod    Pointer to receive the timer period in 100ns units.

  @return EFI_SUCCESS           The period was returned successfully.
  @return EFI_INVALID_PARAMETER TimerPeriod is NULL.
**/
EFI_STATUS
EFIAPI
WatchdogTimerGetTimerPeriod (
  IN EFI_WATCHDOG_TIMER_ARCH_PROTOCOL  *This,
  OUT UINT64                           *TimerPeriod
  )
{
  if (TimerPeriod == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  *TimerPeriod = mWatchdogTimerPeriod;

  return EFI_SUCCESS;
}

/**
  Gets the debug output device for logging.

  Locates the gEfiGenericMemTestPatternGuid HOB to retrieve the
  debug print error level configuration. If the HOB is not found
  or its size exceeds the expected maximum, NULL is returned.

  The first invocation caches the result in mDebugPrintErrorLevel
  for subsequent accesses.

  @return Pointer to the debug output device function table.
          NULL if the HOB entry could not be located or is invalid.
**/
VOID *
GetDebugOutputDevice (
  VOID
  )
{
  VOID   *DebugDevice;
  UINTN   HobSize;

  //
  // Return cached value if already initialized
  //
  DebugDevice = mHobList;
  if (DebugDevice != NULL) {
    return DebugDevice;
  }

  //
  // Allocate a buffer from EFI HOB space to read the HOB entry
  //
  HobSize = (UINTN)gBS->GetHobList ();
  gBS->GetNextHob ();

  if (HobSize <= sizeof (EFI_HOB_GUID_TYPE)) {
    //
    // HOB is too small or invalid; return NULL
    //
    return NULL;
  }

  //
  // Locate the debug output device via protocol lookup
  //
  DebugDevice = NULL;
  gBS->LocateProtocol (
         &gEfiGenericMemTestPatternGuid,
         NULL,
         &DebugDevice
         );
  if (DebugDevice != NULL) {
    //
    // Cache the result
    //
    mHobList = DebugDevice;
  }

  return DebugDevice;
}

/**
  Debug assertion handler that prints the assert message.

  Writes the assert message using the debug output device found by
  GetDebugOutputDevice(). The error level is determined by checking
  the NMI flag via CMOS I/O ports 0x70/0x71.

  The NMI check inspects CMOS index 0x4B. If the current NMI value is
  greater than 3 or is zero, the effective error level is set to 4.
  The error level is then AND-ed with the caller's ErrorLevel parameter.
  If there is a match, the message is dispatched to the debug output
  device.

  @param[in] ErrorLevel  Bitmask of enabled error levels from the caller.
  @param[in] Format      Format string for the assert message.
  @param[in] ...         Variable arguments for the format string.
**/
VOID
DebugAssertPrint (
  IN UINTN       ErrorLevel,
  IN CONST CHAR8 *Format,
  ...
  )
{
  VA_LIST                 Marker;
  VOID                   *DebugDevice;
  UINTN                   EffectiveErrorLevel;
  UINT8                   NmiValue;
  UINT8                   SavedIndex;
  UINT8                   NmiStatus;
  DEBUG_PRINT_ASSERT_FUNCTION  PrintFunction;

  VA_START (Marker, Format);

  //
  // Get the debug output device
  //
  DebugDevice = GetDebugOutputDevice ();
  if (DebugDevice == NULL) {
    VA_END (Marker);
    return;
  }

  //
  // Read CMOS NMI flag to determine assert severity
  //
  // Step 1: Save current CMOS index, mask to keep NMI enabled
  //
  SavedIndex = IoRead8 (0x70);
  IoWrite8 (0x70, SavedIndex & 0x80 | 0x4B);

  //
  // Step 2: Read the NMI status value at CMOS index 0x4B
  //
  NmiValue = IoRead8 (0x71);

  //
  // Step 3: Determine effective error level from NMI value
  //
  NmiStatus = NmiValue;
  if (NmiValue > 3) {
    NmiStatus = NmiValue;
    if (NmiValue == 0) {
      //
      // If NMI value is zero, read from fixed memory location
      //
      NmiStatus = (*(volatile UINT8 *)0xFDAF0490) & 2 | 1;
    }
  }

  //
  // Map NMI status to error level:
  //   NmiStatus == 1 -> EFI_ERROR_MAJOR (0x80000004)
  //   NmiStatus != 1 -> EFI_ERROR_MINOR (0x80000006)
  //
  if ((NmiStatus - 1) <= 0xFD) {
    EffectiveErrorLevel = 4;  // DEBUG_WARN / DEBUG_ERROR level
    if (NmiStatus == 1) {
      EffectiveErrorLevel = 0x80000004;  // EFI_D_ERROR (major)
    } else {
      EffectiveErrorLevel = 0x80000006;  // EFI_D_WARN (minor)
    }
  }

  //
  // Check if the determined error level matches the caller's mask
  //
  if ((EffectiveErrorLevel & ErrorLevel) != 0) {
    //
    // Dispatch to debug output device
    //
    PrintFunction = (DEBUG_PRINT_ASSERT_FUNCTION)((DEBUG_PRINT_PROTOCOL *)DebugDevice)->Print;
    PrintFunction (ErrorLevel, Format, Marker);
  }

  VA_END (Marker);
}

/**
  Standard UEFI assert handler with source file location.

  Called when an ASSERT condition fails. Retrieves the debug output
  device via GetDebugOutputDevice() and if available, calls the
  device's assertion handler with the file name, line number, and
  description of the failed assertion.

  @param[in] FileName     Source file name where the assert occurred.
  @param[in] LineNumber   Line number of the assert.
  @param[in] Description  Description string describing the assert condition.
**/
VOID
DebugAssert (
  IN CONST CHAR8 *FileName,
  IN UINTN        LineNumber,
  IN CONST CHAR8 *Description
  )
{
  VOID   *DebugDevice;
  DEBUG_ASSERT_FUNCTION  AssertFunction;

  //
  // Get the debug output device
  //
  DebugDevice = GetDebugOutputDevice ();
  if (DebugDevice == NULL) {
    return;
  }

  //
  // Call the device's assert function
  //
  AssertFunction = (DEBUG_ASSERT_FUNCTION)((DEBUG_PRINT_PROTOCOL *)DebugDevice)->Assert;
  AssertFunction (FileName, LineNumber, Description);
}

/**
  Retrieves the HOB list pointer from the system configuration table.

  Iterates through the system table's configuration table array to
  find the entry with gEfiHobListGuid. Once found, the HOB list pointer
  is cached in mHobList for all subsequent calls.

  If the HOB list GUID is not found in any configuration table entry,
  an assert is generated.

  @return Pointer to the start of the HOB list.
          NULL if the HOB list could not be found.
**/
VOID *
GetHobList (
  VOID
  )
{
  UINTN      Index;
  UINTN      TableCount;
  EFI_CONFIGURATION_TABLE  *ConfigTable;
  EFI_GUID   *Guid;
  VOID       *HobEntry;

  //
  // Return cached value if already initialized
  //
  if (mHobList != NULL) {
    return mHobList;
  }

  //
  // Initialize to failure state
  //
  mHobList = NULL;
  HobEntry = NULL;

  //
  // Get the number of configuration table entries
  //
  TableCount = gST->NumberOfTableEntries;
  if (TableCount == 0) {
    goto HOB_NOT_FOUND;
  }

  //
  // Walk the configuration table looking for gEfiHobListGuid
  //
  ConfigTable = gST->ConfigurationTable;
  for (Index = 0; Index < TableCount; Index++) {
    Guid = &ConfigTable[Index].VendorGuid;
    if (HobCompareGuidEntry (&gEfiHobListGuid, Guid)) {
      //
      // Found the HOB list
      //
      HobEntry = ConfigTable[Index].VendorTable;
      mHobList = HobEntry;
      return mHobList;
    }
  }

HOB_NOT_FOUND:
  //
  // HOB list not found -- generate assert
  //
  DebugAssertPrint (
    0x80000000,
    "\nASSERT_EFI_ERROR (Status = %r)\n",
    EFI_NOT_FOUND
    );
  DebugAssert (
    __FILE__,
    __LINE__,
    "!EFI_ERROR (Status)"
    );

  if (mHobList == NULL) {
    DebugAssert (
      __FILE__,
      __LINE__,
      "mHobList != ((void *) 0)"
      );
  }

  return mHobList;
}

/**
  Sends an SMM communication command to the watchdog timer SMI handler.

  Locates (or uses the cached instance of) the SMM Communication Protocol
  and sends a command to synchronize watchdog state with the SMM handler.

  The communication buffer contains:
  - Command: SMM_WATCHDOG_COMMUNICATION_COMMAND (0x40000002)
  - Data:    SMM_WATCHDOG_COMMUNICATION_DATA (69635)

  The protocol is located and cached on first use; subsequent calls
  use the cached pointer.

  @return EFI_SUCCESS           The SMM communication was sent successfully.
  @return EFI_UNSUPPORTED       The protocol could not be located or the function is unavailable.
  @return EFI_NOT_FOUND         The Boot Services table is not available.
**/
EFI_STATUS
SmmCommunicationSend (
  VOID
  )
{
  EFI_SMM_COMMUNICATION_PROTOCOL  *SmmComm;
  EFI_STATUS                      Status;
  UINTN                           CommSize;

  //
  // Use cached protocol if available
  //
  SmmComm = (EFI_SMM_COMMUNICATION_PROTOCOL *)mSmmCommunicationProtocol;
  if (SmmComm != NULL) {
    //
    // Send the watchdog command via SMM communication
    //
    CommSize = sizeof (SMM_COMMUNICATION_BUFFER);
    Status = SmmComm->Communicate (
                        SmmComm,
                        &mWatchdogTimerPeriod,
                        &CommSize
                        );
    return Status;
  }

  //
  // First call: validate Boot Services table
  //
  if (gBS == NULL) {
    return EFI_UNSUPPORTED;
  }

  //
  // Locate the SMM Communication Protocol
  //
  Status = gBS->LocateProtocol (
                  &gEfiSmmCommunicationProtocolGuid,
                  NULL,
                  (VOID **)&SmmComm
                  );
  if (EFI_ERROR (Status)) {
    return EFI_UNSUPPORTED;
  }

  //
  // Cache the protocol pointer
  //
  mSmmCommunicationProtocol = (VOID *)SmmComm;

  //
  // Send the watchdog command via SMM communication
  //
  CommSize = sizeof (SMM_COMMUNICATION_BUFFER);
  Status = SmmComm->Communicate (
                      SmmComm,
                      &mWatchdogTimerPeriod,
                      &CommSize
                      );
  return Status;
}

/**
  Reads a 64-bit value from an unaligned memory address.

  This is a utility function that performs a direct 64-bit read
  from the given Buffer address, even if the address is not
  8-byte aligned.

  @param[in] Buffer  Pointer to the unaligned 64-bit value.

  @return The 64-bit value read from Buffer.
**/
UINT64
EFIAPI
ReadUnaligned64 (
  IN CONST VOID  *Buffer
  )
{
  if (Buffer == NULL) {
    DebugAssert (
      __FILE__,
      __LINE__,
      "Buffer != ((void *) 0)"
      );
  }

  return *(UINT64 *)Buffer;
}

/**
  Compares two GUIDs to determine if a HOB entry matches the target GUID.

  Used during HOB list traversal. Reads two GUIDs from memory and
  compares them for equality. The first GUID is read from a fixed
  address (the expected GUID), and the second is read from the
  current HOB entry being examined.

  @param[in] Entry         Pointer to the current HOB entry data.
                           The GUID at offset 0 and offset 8 of the
                           entry are compared against the expected GUID.

  @param[in] ExpectedGuid  Pointer to the HOB entry whose GUID to match.

  @return TRUE   Both halves of the GUID match (GUIDs are equal).
  @return FALSE  The GUIDs differ.
**/
BOOLEAN
HobCompareGuidEntry (
  IN VOID   *Entry,
  IN VOID   *ExpectedGuid
  )
{
  UINT64  EntryGuidPart1;
  UINT64  EntryGuidPart2;
  UINT64  ExpectedGuidPart1;
  UINT64  ExpectedGuidPart2;

  //
  // Read the first 8 bytes of each GUID
  //
  EntryGuidPart1     = ReadUnaligned64 (Entry);
  ExpectedGuidPart1  = ReadUnaligned64 (ExpectedGuid);

  //
  // Read the second 8 bytes of each GUID
  //
  EntryGuidPart2     = ReadUnaligned64 ((UINT8 *)Entry + 8);
  ExpectedGuidPart2  = ReadUnaligned64 ((UINT8 *)ExpectedGuid + 8);

  //
  // Compare both 64-bit halves
  //
  return (BOOLEAN)(EntryGuidPart1 == ExpectedGuidPart1 &&
                   EntryGuidPart2 == ExpectedGuidPart2);
}