Newer
Older
AMI-Aptio-BIOS-Reversed / UsbOcUpdateDxeLightningRidgeEXECB1 / UsbOcUpdateDxeLightningRidgeEXECB1.c
@Ajax Dong Ajax Dong 2 days ago 36 KB Init
/**
 * @file UsbOcUpdateDxeLightningRidgeEXECB1.c
 *
 * @brief UsbOcUpdateDxeLightningRidgeEXECB1 - UEFI DXE driver for USB over-current
 *        (OC) protection configuration on the LightningRidgeEXECB1 platform.
 *
 * MODULE TYPE: DXE Driver (Index 0021 in BIOS FFS)
 * UEFI PHASE:  DXE
 *
 * FLOW SUMMARY:
 *   1. _ModuleEntryPoint() (0x390) initializes UEFI globals, locates the HOB
 *      list via GetHobList(), prints a debug banner, locates the UBA
 *      board-type protocol, and registers the USB OC configuration.
 *   2. GetHobList() (0x5F8) scans SystemTable->ConfigurationTable[] for
 *      EFI_HOB_LIST_GUID using IsHobListGuid().
 *   3. DebugPrint() (0x530) checks CMOS debug level and calls the EDK2 DebugLib
 *      protocol output function.
 *   4. DebugAssert() (0x5B8) calls the DebugLib protocol's assertion handler.
 *   5. GetDebugProtocol() (0x4B0) lazily resolves and caches the DebugLib protocol.
 *   6. ReadUnaligned64() (0x740) reads a 64-bit value from potentially
 *      unaligned memory with a NULL check.
 *
 * Key difference from LightningRidgeEXRP variant:
 *   - ECB1 has 3 USB OC port mapping entries (vs. EXRP's 4)
 *   - The port mapping data differs in the third and fourth entries
 *
 * GUIDs:
 *   - EDK2 DebugLib Protocol: {36232936-0E76-31C8-A13A-3AF2FC1C3932}
 *   - EFI_HOB_LIST_GUID: {7739F24C-93D7-11D4-9A3A-0090273FC14D}
 *   - UBA Board-Type Protocol (shared): {E03E0D46-5263-4845-B0A4-58D57B3177E2}
 *   - UBA LightningRidgeEXECB1 USB OC Config: {2638009E-3850-4E4B-B05D-042A32DBB9D1}
 *
 * HARDWARE:
 *   - CMOS RTC ports 0x70/0x71: Debug level register at index 0x4B
 *   - MMIO 0xFDAF0490: Board configuration register (fallback for debug level)
 */

#include "UsbOcUpdateDxeLightningRidgeEXECB1.h"

// ============================================================================
// Static (Module-Level) Global Variables
// ===========================================================================/

///
/// Cached pointer to the EDK2 DebugLib protocol interface.
/// Initialized lazily by GetDebugProtocol(). Located via gBS->LocateProtocol()
/// against the DebugLib protocol GUID {36232936-0E76-31C8-A13A-3AF2FC1C3932}.
/// Stored at address 0xC70.
///
STATIC VOID  *mDebugProtocol;     // qword_C70 at 0xC70

///
/// Cached pointer to the HOB list.
/// Initialized lazily by GetHobList() by searching the system configuration
/// table for the EFI_HOB_LIST_GUID entry. Stored at address 0xC78.
///
STATIC VOID  *mHobList;           // qword_C78 at 0xC78

///
/// Cached CMOS debug level byte (at 0xC80).
/// Read from CMOS register 0x4B during debug output filtering.
///
STATIC UINT8  mCmosDebugLevel;    // n3 at 0xC80

// ============================================================================
// Constant Data (in .data section)
// ===========================================================================/

//
// These are located in the .data section of the binary. They are referenced
// by absolute address in the compiled code and are provided here for reference.
//

//
// EFI_GUID mDebugLibProtocolGuid         @ 0xB60
//   {36232936-0E76-31C8-A13A-3AF2FC1C3932}
//   EDK2 DebugLib protocol GUID, used with gBS->LocateProtocol() in
//   GetDebugProtocol().
//
// EFI_GUID mUbaProtocolGuid              @ 0xB70
//   {E03E0D46-5263-4845-B0A4-58D57B3177E2}
//   UBA board-type protocol GUID (shared across all UBA variants).
//
// EFI_GUID mUsbOcConfigGuid              @ 0xB80
//   {2638009E-3850-4E4B-B05D-042A32DBB9D1}
//   USB OC config protocol GUID for LightningRidgeEXECB1.
//
// EFI_GUID mEfiHobListGuid               @ 0xB90
//   {7739F24C-93D7-11D4-9A3A-0090273FC14D}
//   Used for HOB list identification.
//
// uint64 mUsbOcHobListFirstHalf          @ 0xB90 (first 8 bytes)
// uint64 mUsbOcHobListSecondHalf         @ 0xB98 (second 8 bytes)
//
// UBA_USBOC_PORT_MAP_ENTRY mUsbOcPortMap[3]  @ 0xBA0
//   [0]: {0x00000000, 0x00000001, 0x00000008, 0x00000008}
//   [1]: {0x00000008, 0x00000002, 0x00000001, 0x00000002}
//   [2]: {0x00000008, 0x00000008, 0x00000008, 0x00000004}
//
//   NOTE: This is 3 entries vs. EXRP's 4. The ECB1 platform has one fewer
//   USB port that requires OC mapping configuration.
//
// UBA_USBOC_CONFIG_DATA mUsbOcConfigHdr  @ 0xBE0
//   Signature: "PUSB" (0x42535550)
//   Version: 2
//   Size: 0x48C (1164 bytes)
//
// Additional USB OC config data @ 0xBF0 (12 x UINT32):
//   Entry 0: {0x00000000, 0x00000001, 0x00000001, 0x00000002}
//   Entry 1: {0x00000003, 0x00000003, 0x00000008, 0x00000008}
//   Entry 2: {0x00000008, 0x00000008, 0x01020007, 0x01020007}
//
//   NOTE: At 0xC1C (offset 0x1C from 0xC00), the data transitions into the
//   repeated pattern 0x01020007 (USB port descriptors) that extends through
//   0xC48. The data at 0xC18-0xC48 is USB device path-like descriptors
//   (repeated pattern: Type=0x07=USB, SubType=0x00, Length[2]=0x0102=258 bytes).
//
// USB OC port descriptor array @ 0xC18:
//   Repeated bytes: 07 00 02 01
//   Interpretation: {0x07, 0x00, 0x02, 0x01} repeated 12 times =
//   Type=7(USB), SubType=0, Length[0]=2, Length[1]=1  (device path node)
//

// ============================================================================
// Local (Forward) Function Declarations
// ===========================================================================/

/**
 * Retrieves the EDK2 DebugLib protocol interface from gBS, caching the result.
 *
 * Performs a UEFI environment validation by allocating and freeing a small pool
 * buffer, then calls gBS->LocateProtocol() with the DebugLib protocol GUID.
 * The result is cached in mDebugProtocol (0xC70).
 *
 * @return Pointer to the DebugLib protocol interface, or NULL if unavailable.
 */
STATIC
VOID *
GetDebugProtocol (
  VOID
  );

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

/**
 * Module entry point for UsbOcUpdateDxeLightningRidgeEXECB1.
 *
 * Initializes UEFI global variables (gImageHandle, gST, gBS, gRT), locates the
 * HOB list via GetHobList(), prints a debug banner via DebugPrint(), locates
 * the UBA board-type protocol, and registers the USB OC configuration data
 * by calling the protocol's RegisterUsbOcConfig function.
 *
 * Calling sequence:
 *   1. gImageHandle = ImageHandle    (if NULL, assertion fires)
 *   2. gST = SystemTable             (if NULL, assertion fires)
 *   3. gBS = SystemTable->BootServices  (if NULL, assertion fires)
 *   4. gRT = SystemTable->RuntimeServices (if NULL, assertion fires)
 *   5. GetHobList(ImageHandle)       (caches HobList pointer)
 *   6. Interface = NULL
 *   7. DebugPrint(DEBUG_INFO, "UBA:UsbOcUpdate-TypeLightningRidgeEXECB1\n")
 *   8. gBS->LocateProtocol(&mUbaProtocolGuid, NULL, &Interface)
 *   9. Interface->RegisterUsbOcConfig(Interface, &mUsbOcConfigGuid,
 *                                     &mUsbOcConfigHdr, sizeof(UBA_USBOC_CONFIG_DATA))
 *
 * @param[in] ImageHandle  The firmware-allocated handle for this driver image.
 * @param[in] SystemTable  A pointer to the EFI System Table.
 *
 * @return EFI_SUCCESS     The USB OC config protocol was registered.
 * @return Other           Returned directly from LocateProtocol if the UBA
 *                         board-type protocol is not available.
 *
 * @note 0x396: Stores ImageHandle in gImageHandle (qword_C60).
 * @note 0x3bb: Stores SystemTable in gST (qword_C50).
 * @note 0x3e1: Reads BootServices from SystemTable + 0x60.
 * @note 0x407: Reads RuntimeServices from SystemTable + 0x58.
 * @note 0x429: Call to GetHobList.
 * @note 0x42e: Zero-initializes local Interface variable on stack.
 * @note 0x440: Call to DebugPrint with "UBA:UsbOcUpdate-TypeLightningRidgeEXECB1\n".
 * @note 0x45a: gBS->LocateProtocol(&mUbaProtocolGuid, NULL, &Interface).
 *              gBS->LocateProtocol = BootServices + 0x140 (offset 320 in bytes,
 *              which is the function at index 40 in the EFI_BOOT_SERVICES struct).
 * @note 0x481: Interface->RegisterUsbOcConfig(Interface, &mUsbOcConfigGuid,
 *              &mUsbOcConfigHdr, 16). The function pointer is at offset 0x10
 *              in the protocol interface.
 */
EFI_STATUS
EFIAPI
_ModuleEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;
  VOID        *Interface;

  //
  // Cache ImageHandle with assertion check.
  // @note 0x396: mov [ImageHandle], rcx
  // @note 0x3a0: test rcx, rcx; jnz .continue
  //
  gImageHandle = ImageHandle;
  if (ImageHandle == NULL) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
      51,
      "gImageHandle != ((void *) 0)"
      );
  }

  //
  // Cache SystemTable with assertion check.
  // @note 0x3bb: mov [SystemTable], rdx
  // @note 0x3c2: test rbx, rbx; jnz .continue
  //
  gST = SystemTable;
  if (SystemTable == NULL) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
      57,
      "gST != ((void *) 0)"
      );
  }

  //
  // Cache BootServices from SystemTable with assertion check.
  // SystemTable->BootServices is at offset 0x60 in EFI_SYSTEM_TABLE.
  // @note 0x3dd: mov rax, [rbx + 0x60]
  // @note 0x3e8: test rax, rax; jnz .continue
  //
  gBS = SystemTable->BootServices;
  if (gBS == NULL) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
      63,
      "gBS != ((void *) 0)"
      );
  }

  //
  // Cache RuntimeServices from SystemTable with assertion check.
  // SystemTable->RuntimeServices is at offset 0x58 in EFI_SYSTEM_TABLE.
  // @note 0x403: mov rax, [rbx + 0x58]
  // @note 0x40e: test rax, rax; jnz .continue
  //
  gRT = SystemTable->RuntimeServices;
  if (gRT == NULL) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\UefiRuntimeServicesTableLib\\UefiRuntimeServicesTableLib.c",
      47,
      "gRT != ((void *) 0)"
      );
  }

  //
  // Locate the HOB list from the system configuration table.
  // This is required for subsequent HOB-based drivers.
  // @note 0x429: call sub_5F8 (GetHobList)
  //
  GetHobList (ImageHandle);

  //
  // Print debug banner indicating this driver is executing.
  // @note 0x434: lea rdx, "UBA:UsbOcUpdate-TypeLightningRidgeEXECB1\n"
  // @note 0x43b: mov ecx, 0x80000000 (DEBUG_INFO error level)
  // @note 0x440: call sub_530 (DebugPrint)
  //
  Interface = NULL;
  DebugPrint (DEBUG_INFO, "UBA:UsbOcUpdate-TypeLightningRidgeEXECB1\n");

  //
  // Locate the UBA board-type protocol.
  // This protocol is shared across all UBA platform variants.
  //
  // @note 0x453: lea rcx, unk_B70  (&mUbaProtocolGuid)
  // @note 0x451: xor edx, edx      (NULL registration key)
  // @note 0x44c: lea r8, [rsp+0x28+arg_0]  (&Interface = output)
  // @note 0x45a: call [rax + 0x140]  (gBS->LocateProtocol)
  //
  Status = gBS->LocateProtocol (
                  &mUbaProtocolGuid,
                  NULL,
                  &Interface
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Call the board-type protocol's RegisterUsbOcConfig function at offset 0x10.
  // This registers the USB OC configuration protocol for LightningRidgeEXECB1.
  //
  // Parameters passed to RegisterUsbOcConfig:
  //   rcx = This         (the protocol interface pointer)
  //   rdx = ConfigGuid   (&mUsbOcConfigGuid)
  //   r8  = ConfigData   (&mUsbOcConfigHdr = "PUSB" configuration header)
  //   r9  = ConfigSize   (16 = sizeof(UBA_USBOC_CONFIG_DATA))
  //
  // @note 0x46a: lea r8, unk_BE0  (&mUsbOcConfigHdr = "PUSB" structure)
  // @note 0x471: mov r9d, 0x10    (size = 16 bytes)
  // @note 0x477: lea rdx, unk_B80 (&mUsbOcConfigGuid)
  // @note 0x47e: mov rcx, rax    (Interface)
  // @note 0x481: call [rax + 0x10]  (Interface->RegisterUsbOcConfig)
  //
  return ((UBA_BOARD_TYPE_PROTOCOL *)Interface)->RegisterUsbOcConfig (
           Interface,
           &mUsbOcConfigGuid,
           &mUsbOcConfigHdr,
           sizeof (UBA_USBOC_CONFIG_DATA)
           );
}

/**
 * Provides USB OC configuration data pointer set.
 *
 * NOTE: This function has ZERO callers in the current module.
 * It is dead code, but is included for completeness. It may be referenced
 * by a function pointer table or intended for external consumption.
 *
 * Returns pointers to the three USB OC configuration data blocks:
 *   - *a1 = &mUsbOcPortMap (USB OC port mapping table at 0xBA0, 3 entries)
 *   - *a2 = &mUsbOcConfigData (additional USB OC config at 0xBF0)
 *   - *a3 = &mUsbOcPortDesc (USB OC port descriptor array at 0xC18)
 *
 * The mapping table at 0xBA0 defines how USB ports map to over-current
 * sense pins (3 entries vs. 4 in the EXRP variant). The data at 0xBF0
 * contains additional configuration parameters. The descriptor array at
 * 0xC18 contains USB device path-like descriptors (repeated pattern
 * 0x07, 0x00, 0x02, 0x01 = USB Type 7, SubType 0).
 *
 * @param[out] a1  Receives address of USB OC port mapping table.
 * @param[out] a2  Receives address of USB OC config data.
 * @param[out] a3  Receives address of USB OC descriptor array.
 *
 * @return 0 (EFI_SUCCESS equivalent).
 *
 * @note 0x493: *a1 = &unk_BA0 (mUsbOcPortMap)
 * @note 0x49d: *a2 = &unk_BF0 (mUsbOcConfigData)
 * @note 0x4a7: *a3 = &unk_C18 (mUsbOcPortDesc)
 */
UINTN
UsbOcGetConfig (
  OUT VOID  **a1,
  OUT VOID  **a2,
  OUT VOID  **a3
  )
{
  *a1 = &mUsbOcPortMap;
  *a2 = &mUsbOcConfigData;
  *a3 = &mUsbOcPortDesc;
  return 0;
}

/**
 * Locates the HOB (Hand-Off Block) list from the UEFI System Table's
 * configuration table array.
 *
 * Iterates through SystemTable->ConfigurationTable[] looking for an entry
 * whose VendorGuid matches EFI_HOB_LIST_GUID. The comparison is done by
 * comparing the first 8 bytes and second 8 bytes of the GUID as 64-bit
 * integers via ReadUnaligned64().
 *
 * The system's configuration table is an array of EFI_CONFIGURATION_TABLE
 * entries, each 24 bytes (0x18):
 *   +0x00: EFI_GUID VendorGuid (16 bytes)
 *   +0x10: VOID *VendorTable   (8 bytes)
 *
 * Results are cached in mHobList (0xC78). If the HOB list GUID is not found,
 * an ASSERT_EFI_ERROR is raised via DebugPrint and DebugAssert.
 *
 * @param[in] ImageHandle  Passed through from entry but unused in the loop.
 *                         In this module, the ImageHandle parameter is passed
 *                         directly to IsHobListGuid(), which does not use it
 *                         either (it only compares GUIDs).
 *
 * @return Pointer to the HOB list, or NULL if not found.
 *
 * @note 0x607: Load mHobList from global at 0xC78; return if non-NULL (cached).
 * @note 0x617: Load gST from global at 0xC50.
 * @note 0x627: gST->NumberOfTableEntries is at gST + 0x68 = +104 (offset of
 *              NumberOfTableEntries in EFI_SYSTEM_TABLE).
 * @note 0x63d: gST->ConfigurationTable is at gST + 0x70 = +112.
 *              Each table entry is 0x18 (24) bytes.
 *              Loop variable v4 increments by 24 each iteration.
 * @note 0x63d: Call IsHobListGuid(ImageHandle, &ConfigTable[Index].VendorGuid).
 * @note 0x6c0: If match found: extract VendorTable pointer at ConfigTable[Index]+16.
 * @note 0x64c: If not found: DebugPrint(0x800000000000000E = EFI_NOT_FOUND,
 *              "\\nASSERT_EFI_ERROR (Status = %r)\\n")
 * @note 0x67a: DebugAssert from HobLib.c line 54: "!EFI_ERROR (Status)"
 * @note 0x6a1: DebugAssert from HobLib.c line 55: "mHobList != ((void *) 0)"
 */
VOID *
GetHobList (
  IN EFI_HANDLE  ImageHandle
  )
{
  UINTN Index;
  UINTN TableCount;
  EFI_CONFIGURATION_TABLE  *ConfigTable;

  //
  // Return cached value if already resolved.
  // @note 0x607: mov rax, mHobList; test rax, rax; jnz .return_cache
  //
  if (mHobList != NULL) {
    return mHobList;
  }

  //
  // Initialize HOB list pointer to NULL.
  // @note 0x620: mov mHobList, 0
  //
  mHobList = NULL;

  //
  // Get the number of configuration table entries.
  // gST + 0x68 = SystemTable->NumberOfTableEntries
  // @note 0x627: cmp [rbx + 0x68], 0
  //
  TableCount = gST->NumberOfTableEntries;

  //
  // If there are entries, scan them for EFI_HOB_LIST_GUID.
  //
  if (TableCount > 0) {
    //
    // Get pointer to the configuration table array.
    // gST + 0x70 = SystemTable->ConfigurationTable
    // @note 0x62d: mov rsi, 0  (Index = 0)
    //
    ConfigTable = gST->ConfigurationTable;

    //
    // Iterate through configuration table entries.
    // Each iteration:
    //   - Calls IsHobListGuid(ImageHandle, &ConfigTable[Index].VendorGuid)
    //     at offset 0x63d
    //   - If match found, extracts VendorTable pointer and caches it
    //   - Otherwise increments Index (rsi = v3++) and
    //     advances v4 (rbp) by 0x18 (24 = sizeof(EFI_CONFIGURATION_TABLE))
    //
    for (Index = 0; Index < TableCount; Index++, ConfigTable++) {
      //
      // Compare current entry's VendorGuid against EFI_HOB_LIST_GUID.
      // The comparison splits the 16-byte GUID into two 8-byte halves:
      //   - First 8 bytes  (GUID.Data1 + GUID.Data2 + GUID.Data3 high)
      //   - Second 8 bytes (GUID.Data3 low + GUID.Data4)
      //
      // @note 0x63d: call IsHobListGuid(ImageHandle, &ConfigTable[Index].VendorGuid)
      //
      if (IsHobListGuid (ImageHandle, &ConfigTable->VendorGuid)) {
        //
        // Found the HOB list entry. Extract the VendorTable pointer.
        //
        // @note 0x6c0: rax = ConfigTable[Index] + 16 = VendorTable pointer
        //
        mHobList = ConfigTable->VendorTable;
        return mHobList;
      }
    }
  }

  //
  // HOB list GUID not found in configuration table.
  // Raise ASSERT_EFI_ERROR with EFI_NOT_FOUND (0x800000000000000E).
  //
  // The EFI_NOT_FOUND high bit (63) ensures it's recognized as an error
  // by the EFI_ERROR() macro, and it's printed as the format argument
  // "\nASSERT_EFI_ERROR (Status = %r)\n" where %r is a UEFI status code.
  //
  // @note 0x64c: DebugPrint(EFI_NOT_FOUND, "\nASSERT_EFI_ERROR (Status = %r)\n")
  //
  DebugPrint (EFI_NOT_FOUND, "\nASSERT_EFI_ERROR (Status = %r)\n");
  DebugAssert (
    "e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
    54,
    "!EFI_ERROR (Status)"
    );

  //
  // If mHobList is still NULL after the search, raise another assertion.
  // This would indicate the HOB list GUID entry exists but has a NULL
  // table pointer, which is an unexpected condition.
  //
  if (mHobList == NULL) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
      55,
      "mHobList != ((void *) 0)"
      );
  }

  return mHobList;
}

/**
 * Compares a GUID against the EFI_HOB_LIST_GUID by comparing its first 8 bytes
 * and second 8 bytes independently.
 *
 * Instead of a full 16-byte EFI_GUID comparison, this function uses two
 * 8-byte unaligned reads (ReadUnaligned64) to compare the GUID halves against
 * the pre-cached values at mUsbOcHobListFirstHalf (unk_B90) and
 * mUsbOcHobListSecondHalf (unk_B98).
 *
 * The GUID halves are derived from EFI_HOB_LIST_GUID:
 *   {7739F24C-93D7-11D4-9A3A-0090273FC14D}
 *   First 8 bytes (little-endian): 0x11D493D77739F24C = {0x7739F24C, 0x93D7}
 *   Second 8 bytes:                0x4DC13F2700903A9A = {0x11D4 ... 0x3FC14D}
 *
 * @param[in] ImageHandle  Unused parameter passed through from GetHobList()
 *                         (matches the convention of other UBA modules where
 *                         this is a callback-compatible signature).
 * @param[in] GuidPtr      Pointer to the EFI_GUID to compare. Must not be NULL.
 *
 * @retval TRUE   The GUID at GuidPtr matches EFI_HOB_LIST_GUID.
 * @retval FALSE  The GUID does not match.
 *
 * @note This function implements an optimized GUID comparison that avoids
 *       calling the full CompareGuid() function from BaseLib.
 * @note 0x6f6: First call to sub_740(&unk_B90) = ReadUnaligned64(first half)
 * @note 0x705: Second call to sub_740(a2) = ReadUnaligned64(candidate first half)
 * @note 0x711: Third call to sub_740(&unk_B98) = ReadUnaligned64(second half)
 * @note 0x714: Fourth call to sub_740(a2 + 8) = ReadUnaligned64(candidate second half)
 * @note 0x738: cmp rdi, rbx (compare first halves), cmovnz, then cmp rbp, rax
 *              (compare second halves), yielding boolean result.
 */
BOOLEAN
IsHobListGuid (
  IN EFI_HANDLE  ImageHandle,
  IN EFI_GUID    *GuidPtr
  )
{
  //
  // Compare first 8 bytes of the GUID.
  // These are at unk_B90 in the .data section (offset 0xB90).
  // The data at 0xB90 is the first 8 bytes of EFI_HOB_LIST_GUID:
  //   Byte: 4C F2 39 77 D7 93 D4 11
  //   As UINT64: 0x11D493D77739F24C
  //
  // @note 0x6f6: call sub_740(&unk_B90) -- reads mUsbOcHobListFirstHalf
  // @note 0x705: call sub_740(a2) -- reads first half of candidate GUID
  // @note 0x738: cmp rdi, rbx -- compare first halves
  //
  if (ReadUnaligned64 (&mUsbOcHobListFirstHalf) != ReadUnaligned64 (GuidPtr)) {
    return FALSE;
  }

  //
  // Compare second 8 bytes of the GUID.
  // These are at unk_B98 in the .data section (offset 0xB98).
  // The data at 0xB98 is the second 8 bytes of EFI_HOB_LIST_GUID:
  //   Byte: 9A 3A 00 90 27 3F C1 4D
  //   As UINT64: 0x4DC13F2700903A9A
  //
  // GuidPtr + 8 points to the second half of the 16-byte GUID structure.
  // In the SystemTable configuration table array, each entry is 24 bytes:
  //   16 bytes for GUID followed by 8 bytes for VendorTable pointer.
  // So GuidPtr + 8 is the second half of the GUID, and GuidPtr + 16
  // would be the VendorTable pointer.
  //
  // @note 0x711: call sub_740(&unk_B98) -- reads mUsbOcHobListSecondHalf
  // @note 0x714: call sub_740(a2 + 8) -- reads second half of candidate GUID
  // @note 0x738: cmp rbp, rax -- compare second halves
  //
  return ReadUnaligned64 (&mUsbOcHobListSecondHalf) == ReadUnaligned64 ((UINT8 *)GuidPtr + 8);
}

/**
 * Debug print function.
 *
 * Resolves the DebugLib protocol interface via GetDebugProtocol(), checks the
 * CMOS debug level to determine if the requested error level is enabled, and
 * if so, calls the DebugLib protocol's output function (first function pointer,
 * at offset 0x00 of the protocol interface).
 *
 * The CMOS debug level is read from RTC CMOS register 0x4B:
 *   - Bit 7 is preserved for RTC NMI enable
 *   - Register index is set to 0x4B (ORed with 0x4B after masking with 0xCB)
 *   - Values > 3: use cached mCmosDebugLevel; if that is 0, read from
 *     MMIO 0xFDAF0490 and compute (register & 2) | 1
 *   - Level 1 -> mask = 0x80000004 (DEBUG_INIT | DEBUG_INFO)
 *   - Level > 1 -> mask = 0x80000046 (multiple debug bits)
 *   - If the ErrorLevel matches the mask, call the protocol's output function
 *
 * @param[in] ErrorLevel  The debug error level mask to check.
 * @param[in] Format      A format string for the debug message.
 * @param[in] ...         Variable arguments for the format string.
 *
 * @return The return value from the DebugLib protocol's output function,
 *         or 0 if the protocol is not available or the error level is
 *         not enabled.
 *
 * @note 0x547: Call GetDebugProtocol() to resolve DebugLib interface.
 * @note 0x55b: IoRead8(RTC_INDEX_PORT) -- read current CMOS index
 * @note 0x560: (CmosValue & 0xCB) | CMOS_DEBUG_LEVEL_REGISTER
 *              0xCB mask preserves bits 7,3,1,0; 0x4B OR sets register addr
 * @note 0x565: IoWrite8(RTC_INDEX_PORT, maskedIndex); IoRead8(RTC_DATA_PORT)
 * @note 0x576-0x582: Fallback read from MMIO 0xFDAF0490 if CMOS value is 0
 *              The read is a UINT32 at address 0xFDAF0490, then &2|1 applied
 * @note 0x586: DebugLevel - 1 check (must be <= 0xFD)
 * @note 0x593: DebugMask = 0x80000046 (for level > 1: DEBUG_INIT | DEBUG_WARN | DEBUG_ERROR | DEBUG_INFO)
 * @note 0x599: DebugMask = 0x80000004 (for level == 1: DEBUG_INIT | DEBUG_INFO)
 * @note 0x5a0: test (DebugMask & ErrorLevel); jz .skip
 * @note 0x5af: call [r9] -- call DebugLib protocol output function at offset 0x00
 *              Parameters: rcx=ErrorLevel, rdx=Format, r8=VA_LIST
 */
UINTN
EFIAPI
DebugPrint (
  IN UINTN       ErrorLevel,
  IN CONST CHAR8 *Format,
  ...
  )
{
  UINTN   ReturnValue;
  UINT8   CmosValue;
  UINT64  DebugLevel;
  UINT32  DebugMask;
  VOID    *DebugProtocol;
  VA_LIST VaList;

  VA_START (VaList, Format);

  //
  // Get the DebugLib protocol interface (lazily resolved and cached).
  // @note 0x547: call sub_4B0 (GetDebugProtocol)
  //
  DebugProtocol = GetDebugProtocol ();
  ReturnValue = 0;

  if (DebugProtocol != NULL) {
    //
    // Read debug level from CMOS register 0x4B.
    // Access RTC CMOS ports 0x70/0x71:
    //   Port 0x70 = CMOS index/address register
    //   Port 0x71 = CMOS data register
    //
    // Step 1: Read current CMOS index register value.
    //         This preserves the NMI enable (bit 7) from whatever was set
    //         by the caller.
    //
    // Step 2: Mask to preserve specific bits and set register address to 0x4B.
    //         and al, 0xCB:
    //           - 0xCB = 0b11001011
    //           - Clears bits 2, 4, 5 (0x34 inverted = 0xCB bits 2=0,4=0,5=0)
    //         or al, 0x4B:
    //           - 0x4B = 0b01001011 = CMOS register index 0x4B
    //           - Sets NMI enable if bit 7 was preserved from original read
    //
    // @note 0x55b: in al, 0x70 -- read current CMOS index
    // @note 0x560: and al, 0xCB -- mask: clear bits 2, 4, 5
    // @note 0x560: or al, 0x4B -- set register address to 0x4B
    // @note 0x565: out 0x70, al -- write to CMOS index port
    //
    CmosValue = IoRead8 (RTC_INDEX_PORT);
    CmosValue = (CmosValue & 0xCB) | CMOS_DEBUG_LEVEL_REGISTER;
    IoWrite8 (RTC_INDEX_PORT, CmosValue);

    //
    // Read the debug level value from CMOS data port 0x71.
    //
    // @note 0x565: in al, 0x71 -- read from CMOS data port
    //
    DebugLevel = IoRead8 (RTC_DATA_PORT);

    //
    // Determine the debug mask based on the CMOS debug level.
    //
    // @note 0x56c: cmp DebugLevel, 3; ja .check_cached
    //
    if (DebugLevel > 3) {
      //
      // For values > 3, check the cached CMOS debug level from a previous
      // read (mCmosDebugLevel at 0xC80). This cached value may be stale
      // but avoids re-reading the CMOS port.
      //
      // @note 0x56e: movzx edx, byte_D80 (mCmosDebugLevel)
      //
      DebugLevel = mCmosDebugLevel;
      if (DebugLevel == 0) {
        //
        // If the cached level is 0, read the board configuration from
        // the MMIO register at 0xFDAF0490. This register contains
        // platform-specific board configuration bits.
        //
        // The formula is: (register & 2) | 1
        //   - Bit 1 indicates some board variant
        //   - Bit 0 is always set
        //   - Result is 1 or 3 depending on bit 1
        //
        // @note 0x576-0x582: Read UINT32 from 0xFDAF0490, & 2, | 1
        //
        DebugLevel = (*(volatile UINT32 *)BOARD_CONFIG_MMIO_ADDR & 2) | 1;
      }
    }

    //
    // Calculate the debug mask from the debug level.
    // The level must be >= 1 and <= 0xFE (since level-1 <= 0xFD).
    // Level 0xFF would underflow and fail the comparison.
    //
    // @note 0x586: n3_1 - 1 (level - 1)
    // @note 0x58a: cmp (n3_1 - 1), 0xFD; ja .skip
    //
    if ((DebugLevel > 0) && ((DebugLevel - 1) <= 0xFD)) {
      //
      // Level 1 -> mask = 0x80000004 (DEBUG_INIT | DEBUG_INFO)
      //   Used for diagnostic output in release builds
      // Level > 1 -> mask = 0x80000046 (DEBUG_INIT | DEBUG_WARN | DEBUG_ERROR | DEBUG_INFO)
      //   Used for full debug output including warnings and errors
      //
      // @note 0x593: v4 = 0x80000046 (for level > 1)
      // @note 0x599: v4 = 0x80000004 (if level == 1)
      //
      if (DebugLevel == 1) {
        DebugMask = 0x80000004;
      } else {
        DebugMask = 0x80000046;
      }

      //
      // Check if the requested ErrorLevel is enabled by the debug mask.
      // If the bit set in ErrorLevel matches any bit in DebugMask,
      // the output is enabled.
      //
      // Common error levels:
      //   0x80000000 = DEBUG_INFO (high bit = general debug)
      //   0x00000040 = DEBUG_INIT
      //   0x00000004 = DEBUG_WARN
      //   0x00000002 = DEBUG_ERROR
      //
      // @note 0x5a0: test (v4 & a1); jz .skip
      //
      if ((DebugMask & ErrorLevel) != 0) {
        //
        // Call the DebugLib protocol's output function.
        // Protocol interface layout:
        //   [v5 + 0x00] = DebugPrint function pointer
        //   [v5 + 0x08] = DebugAssert function pointer
        //
        // The output function takes:
        //   rcx = ErrorLevel
        //   rdx = Format string
        //   r8  = VA_LIST (variable argument list from varargs)
        //
        // @note 0x5af: call [r9] -- call DebugLib protocol output function
        //              where r9 = DebugProtocol (at offset 0x00 of protocol)
        //
        ReturnValue = ((DEBUGLIB_PROTOCOL *)DebugProtocol)->DebugPrint (
                       ErrorLevel,
                       Format,
                       VaList
                       );
      }
    }
  }

  return ReturnValue;
}

/**
 * ASSERT assertion failure handler.
 *
 * Called when a runtime assertion fails (e.g., NULL pointer check, status
 * code validation). Resolves the DebugLib protocol via GetDebugProtocol()
 * and calls its assertion failure handler function at offset 0x08 in the
 * protocol interface.
 *
 * The assertion handler takes:
 *   rcx = FileName (source file name where assertion occurred)
 *   rdx = LineNumber (line number in source file)
 *   r8  = Description (assertion description string)
 *
 * @param[in] FileName     Source file name where the assertion occurred.
 * @param[in] LineNumber   Line number of the assertion.
 * @param[in] Description  Description of the failed assertion.
 *
 * @return 0 if the DebugLib protocol is not available, or the return value
 *         from the assertion handler otherwise.
 *
 * @note 0x5cb: mov rax, [result + 0x00] (this is DebugProtocol pointer)
 * @note 0x5d0: call GetDebugProtocol() to resolve DebugLib
 * @note 0x5e3: call [result + 8] -- call DebugLib protocol's Assert function
 *              at offset 0x08 of the protocol interface.
 *              Parameters: rcx=FileName, rdx=LineNumber, r8=Description
 */
UINTN
DebugAssert (
  IN CONST CHAR8  *FileName,
  IN UINTN        LineNumber,
  IN CONST CHAR8  *Description
  )
{
  VOID   *DebugProtocol;
  UINTN  Result;

  DebugProtocol = GetDebugProtocol ();
  Result = 0;

  if (DebugProtocol != NULL) {
    //
    // Call the DebugLib protocol's assertion handler at offset 0x08.
    //
    // The assertion handler signature:
    //   rcx = FileName
    //   rdx = LineNumber
    //   r8  = Description
    //
    // Unlike DebugPrint which has a variable argument list, DebugAssert
    // has a fixed 3-argument signature, matched by this call.
    //
    Result = ((DEBUGLIB_PROTOCOL *)DebugProtocol)->DebugAssert (
              FileName,
              LineNumber,
              Description
              );
  }

  return Result;
}

/**
 * Retrieves the EDK2 DebugLib protocol interface from gBS, caching the result.
 *
 * Performs a UEFI environment validation by allocating a boot services data
 * buffer (pool type = 31 = EfiBootServicesData) using gBS->AllocatePool
 * (BootServices + 0x18 = 24) and immediately freeing it with gBS->FreePool
 * (BootServices + 0x20 = 32).
 *
 * This pool allocation/free cycle serves as a size check: if the allocation
 * succeeds and the returned buffer is <= 16 bytes (0x10), it indicates a valid
 * UEFI environment. If the buffer address exceeds 16 bytes, NULL is returned
 * as a guard against non-standard UEFI implementations.
 *
 * Then calls gBS->LocateProtocol() (BootServices + 0x140 = 320 = offset 40
 * in EFI_BOOT_SERVICES) to obtain the DebugLib protocol interface.
 * The DebugLib protocol GUID is {36232936-0E76-31C8-A13A-3AF2FC1C3932}.
 * The result is cached in mDebugProtocol (0xC70).
 *
 * @return Pointer to the DebugLib protocol interface, or NULL if unavailable
 *         or if the pool check indicates an invalid UEFI environment.
 *
 * @note 0x4c7: call [BootServices + 0x18] = gBS->AllocatePool(
 *               PoolType=31=EfiBootServicesData, Size=0, &Buffer)
 *              NOTE: The UEFI spec defines AllocatePool with 3 args:
 *              PoolType, Size, Buffer. However the original decompilation
 *              shows Size=0, suggesting only a "probe" allocation to see
 *              if boot services are functional.
 * @note 0x4ca: call [BootServices + 0x20] = gBS->FreePool(Buffer)
 * @note 0x4e2: if (Buffer <= 0x10) -- environment validation guard
 *              If the UEFI environment returns a sensible address (<= 16 bytes),
 *              proceed with LocateProtocol.
 * @note 0x4ee: call [BootServices + 0x140] = gBS->LocateProtocol(
 *              &mDebugLibProtocolGuid, NULL, &mDebugProtocol)
 *              where mDebugLibProtocolGuid = {36232936-0E76-31C8-A13A-
 *              3AF2FC1C3932}
 * @note 0x506: On failure (Status < 0), sets mDebugProtocol to NULL.
 */
STATIC
VOID *
GetDebugProtocol (
  VOID
  )
{
  VOID         *Buffer;
  EFI_STATUS   Status;

  //
  // Return cached value if already resolved.
  // @note 0x4c6: cmp [mDebugProtocol], 0; jnz .return_cached
  //
  if (mDebugProtocol != NULL) {
    return mDebugProtocol;
  }

  //
  // Allocate a small pool buffer (EfiBootServicesData = 31) and free it.
  //
  // This is a UEFI environment validation check: if the allocation succeeds
  // and the buffer address is within a reasonable range (<= 0x10), it indicates
  // a properly functioning UEFI environment where boot services are available.
  //
  // The allocation of Size=0 may seem unusual, but it serves as a probe:
  //   - If UEFI boot services are functional, the AllocatePool call will return
  //     a valid buffer address (even for size 0, some UEFI implementations
  //     return a minimal allocation).
  //   - The buffer is immediately freed, so no memory leak occurs.
  //   - The buffer address range check determines if we're running on real
  //     UEFI hardware-emulation or a simpler environment.
  //
  // On minimal or non-UEFI environments, the allocation may behave differently,
  // returning a very high address or failing.
  //
  // @note 0x4df: mov ecx, 31  (EfiBootServicesData pool type)
  // @note 0x4df: call [BootServices + 0x18]  (AllocatePool)
  // @note 0x4e2: call [BootServices + 0x20]  (FreePool)
  //
  Buffer = NULL;
  gBS->AllocatePool (EfiBootServicesData, 0, &Buffer);
  gBS->FreePool (Buffer);

  if ((UINTN)Buffer <= 0x10) {
    //
    // The buffer size check suggests we are in a valid UEFI environment with
    // properly functioning boot services.
    // Locate the DebugLib protocol via gBS->LocateProtocol.
    //
    // The DebugLib protocol GUID {36232936-0E76-31C8-A13A-3AF2FC1C3932}
    // is stored at address 0xB60 in the .data section.
    //
    // @note 0x4ee: call [BootServices + 0x140]  (LocateProtocol)
    // @note 0x4f2: Protocol GUID at unk_B60
    //
    Status = gBS->LocateProtocol (
                  &mDebugLibProtocolGuid,
                  NULL,
                  &mDebugProtocol
                  );
    //
    // On failure, clear the cached pointer.
    // @note 0x506: test Status; jns .success; mov [mDebugProtocol], 0
    //
    if (EFI_ERROR (Status)) {
      mDebugProtocol = NULL;
    }
  } else {
    //
    // Buffer address exceeds 16 bytes - non-standard UEFI environment.
    // Return NULL, all debug output will be suppressed.
    //
    mDebugProtocol = NULL;
  }

  return mDebugProtocol;
}

/**
 * Reads an unaligned 64-bit value from memory.
 *
 * Wraps the BaseLib ReadUnaligned64() concept with a NULL pointer check.
 * If the Buffer pointer is NULL, raises an assertion.
 *
 * The function performs a simple volatile 64-bit read. On x86-64, unaligned
 * reads are natively supported, so no special alignment handling is needed.
 *
 * @param[in] Buffer  Pointer to the memory to read. Must not be NULL.
 *
 * @return The 64-bit value read from the given address.
 *
 * @note 0x74c: test rcx, rcx; jnz .read; call DebugAssert at
 *              e:\hs\MdePkg\Library\BaseLib\Unaligned.c:192
 * @note 0x769: return *(volatile UINT64 *)Buffer (direct memory read)
 */
UINT64
ReadUnaligned64 (
  IN CONST VOID  *Buffer
  )
{
  //
  // Check for NULL pointer.
  // @note 0x74c: if (Buffer == NULL) call DebugAssert
  //
  if (Buffer == NULL) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\BaseLib\\Unaligned.c",
      192,
      "Buffer != ((void *) 0)"
      );
  }

  //
  // Perform a direct 64-bit read. On x86-64, unaligned memory accesses
  // are handled natively by the processor, so no special handling is
  // needed. The volatile qualifier ensures the compiler performs the
  // read exactly once.
  //
  // @note 0x769: return *Buffer
  //
  return *(volatile UINT64 *)Buffer;
}