Newer
Older
AMI-Aptio-BIOS-Reversed / IioCfgUpdateDxeLightningRidgeEXECB2 / IioCfgUpdateDxeLightningRidgeEXECB2.c
@Ajax Dong Ajax Dong 2 days ago 38 KB Init
/**
 * @file IioCfgUpdateDxeLightningRidgeEXECB2.c
 *
 * @brief IioCfgUpdateDxeLightningRidgeEXECB2 - UEFI DXE driver for registering
 *        IIO configuration data on LightningRidgeEXECB2 platform.
 *
 * MODULE TYPE: DXE Driver
 * UEFI PHASE:  DXE (Driver Execution Environment)
 *
 * PURPOSE:
 *   Registers up to 4 IIO (Integrated IO) configuration protocol instances
 *   through the UBA (Unified Board Architecture) framework. Each instance
 *   corresponds to a CPU socket's IIO configuration table.
 *
 * SOURCE PATH (build machine):
 *   e:\hs\Build\HR6N0XMLK\DEBUG_VS2015\X64\PurleyRpPkg\Uba\UbaMain\Dxe\
 *   TypeLightningRidgeEXECB2\IioCfgUpdateDxe\IioCfgUpdateDxe\
 *   DEBUG\IioCfgUpdateDxeLightningRidgeEXECB2.pdb
 *
 * PLATFORM: LightningRidgeEXECB2
 * IIO CFG TABLE: 19 entries of 12 bytes = 228 bytes
 *
 * @see IioCfgUpdateDxeLightningRidgeEXECB2.h for type definitions and GUIDs.
 */

#include "IioCfgUpdateDxeLightningRidgeEXECB2.h"

// ============================================================================
// Module Globals (.data at 0xC08-0xC28)
// ============================================================================

///
/// Cached debug protocol interface (initialized on first use by GetDebugProtocol).
/// Address in binary: qword_DC0 at 0xDC0 (in .data).
///
VOID *gDebugProtocol = NULL;

///
/// Cached HOB list pointer (initialized on first use by GetHobList).
/// Address in binary: qword_DC8 at 0xDC8 (in .data).
///
VOID *gHobList = NULL;

///
/// Cached board type from CMOS (at 0xDD0 in .data).
/// Used by DebugPrint() when CMOS register 0x4B returns a value >3.
/// The non-obvious behavior: when CMOS value > 3, this cached value
/// overrides the CMOS read. If this cached value is 0, falls back
/// to MMIO register at 0xFDAF0490 for the board type.
///
UINT8 gBoardType = 0;

// ============================================================================
// GUID Instances (.data at 0xBC0-0xBAF)
// ============================================================================

///
/// EFI_HOB_LIST_GUID instance - used to locate HOB list in config table.
/// Address in binary: unk_C10 at 0xC10 (.data).
///
EFI_GUID gEfiHobListGuid = EFI_HOB_LIST_GUID;

///
/// UBA Board-Type Protocol GUID - interface to register config data.
/// Address in binary: unk_BF0 at 0xBF0 (.data).
///
EFI_GUID gUbaBoardTypeProtocolGuid = UBA_BOARD_TYPE_PROTOCOL_GUID;

///
/// UBA IIO Config Update protocol GUIDs (one per CPU socket).
/// Addresses: unk_BC0 at 0xBC0, unk_BE0 at 0xBE0, unk_C00 at 0xC00,
///            unk_C20 at 0xC20 (.data).
///
EFI_GUID gIioCfgUpdateProtocolGuid[IIO_CFG_UPDATE_PROTOCOL_COUNT] = {
  UBA_IIO_CFG_UPDATE_PROTOCOL_GUID_0,
  UBA_IIO_CFG_UPDATE_PROTOCOL_GUID_1,
  UBA_IIO_CFG_UPDATE_PROTOCOL_GUID_2,
  UBA_IIO_CFG_UPDATE_PROTOCOL_GUID_3
};

///
/// UBA Debug Protocol GUID - protocol for debug output.
/// Address in binary: unk_BD0 at 0xBD0 (.data).
///
EFI_GUID gUbaDebugProtocolGuid = UBA_DEBUG_PROTOCOL_GUID;

// ============================================================================
// IIO Configuration Protocol Header (PIIO structure at 0xC30)
// ============================================================================

///
/// UBA_IIO_CFG_UPDATE_PROTOCOL - The IIO config protocol structure
/// registered for each CPU socket on this platform.
///
/// This structure describes the IIO configuration table layout:
///   - Signature: "PIIO" (0x4F494950)
///   - Version:   1
///   - TotalSize: 0x0D60 (size of entire PIIO data region)
///   - ConfigTableOffset: 0x3C (60 bytes from struct base to cfg table)
///   - CallbackFunction:  (VOID *)ReturnNotFound (sub_50C, no-op)
///   - ResourceTableOffset: 0x0C60 (offset to resource table)
///   - ResourceTableSize: 0xFC (252 bytes)
///
STATIC CONST UBA_IIO_CFG_UPDATE_PROTOCOL mIioCfgProtocol = {
  UBA_IIO_CFG_UPDATE_SIGNATURE,
  1,                                    // Version
  0x0D60,                                // TotalSize
  0x3C,                                  // ConfigTableOffset
  (UINT64)(UINTN)ReturnNotFound,         // sub_50C = ReturnNotFound
  0x0C60,                                // ResourceTableOffset
  0xFC                                   // ResourceTableSize
};

// ============================================================================
// IIO Configuration Table (PIIO config entries, at 0xC6C)
// ============================================================================

///
/// IIO configuration table for LightningRidgeEXECB2 (19 entries, 228 bytes).
///
/// KEY DIFFERENCES FROM EXECB1:
///   - Entry [0] is 0x02 (no 0x01 entry) -> UPT slot 0, port 0x01 on 0x4C
///   - No trailing entry 0x48
///   - 19 entries total vs EXECB1's 21 entries
///
/// Entry format (12 bytes each, UBA_IIO_CFG_UPDATE_TABLE_ENTRY):
///   Byte 0:    Type (UPT index)
///   Byte 1:    Reserved0 (typically 0xFF = unchecked)
///   Bytes 2-3: Reserved1 (typically 0x0000)
///   Bytes 4-7: Reserved2 (typically 0x0000FFFF)
///   Byte 8:    Value (0x00 = disabled, 0x01 = enabled)
///   Byte 9:    Port (PCIe port number)
///   Bytes 10-11: AddressType (PCIE controller: 0x4C, 0x4E, 0x40, 0x42)
///
STATIC CONST UINT8 mIioCfgTable[] = {
  //  [0]: UPT 0x02 -> PCIE(0x4C):Slot=01,Port=01
  0x02, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x01, 0x01, 0x4C, 0x01,
  //  [1]: UPT 0x03 -> PCIE(0x4E):Slot=01,Port=00
  0x03, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x01, 0x00, 0x4E, 0x01,
  //  [2]: UPT 0x04 -> PCIE(0x4E):Slot=01,Port=01
  0x04, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x01, 0x01, 0x4E, 0x01,
  //  [3]: UPT 0x05 -> Disabled (Value=0, no port/type)
  0x05, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
  //  [4]: UPT 0x09 -> PCIE(0x40):port=00
  0x09, 0x01, 0x01, 0xFF, 0xFF, 0x01, 0x00, 0x40, 0x00, 0xFF, 0xFF, 0x00,
  //  [5]: UPT 0x15 -> Disabled (Value=0)
  0x15, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x01,
  //  [6]: UPT 0x16 -> Active
  0x16, 0x05, 0x01, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
  //  [7]: UPT 0x1A -> Active
  0x1A, 0x07, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
  //  [8]: UPT 0x1E -> PCIE(0x40), port=0x00, enabled
  0x1E, 0x0A, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x01, 0x00, 0x40, 0x00,
  //  [9]: UPT 0x1F -> PCIE(0x40):Slot=01,Port=01
  0x1F, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x01, 0x01, 0x40, 0x01,
  // [10]: UPT 0x20 -> PCIE(0x42):Slot=01,Port=00
  0x20, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x01, 0x00, 0x42, 0x01,
  // [11]: UPT 0x21 -> PCIE(0x42):Slot=01,Port=01
  0x21, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x01, 0x01, 0x42, 0x01,
  // [12]: UPT 0x2A -> Active (no PCIe type)
  0x2A, 0x0E, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
  // [13]: UPT 0x2B -> Active (no PCIe type)
  0x2B, 0x06, 0x01, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
  // [14]: UPT 0x2F -> Active (no PCIe type)
  0x2F, 0x0F, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
  // [15]: UPT 0x33 -> Active (no PCIe type)
  0x33, 0x08, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
  // [16]: UPT 0x3F -> Disabled (Value=0)
  0x3F, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x01,
  // [17]: UPT 0x40 -> Active (no PCIe type)
  0x40, 0x13, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
  // [18]: UPT 0x44 -> Active (no PCIe type)
  0x44, 0x04, 0x01, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
};

// ============================================================================
// Static Data: UBA Board Resource Table (at 0xD60)
// ============================================================================

///
/// UBA board resource lookup table at offset 0xD60 (part of PIIO data region).
/// Provides mapping between IIO resources and board interface connections.
/// Format: 5 bytes per entry (socket, src_if, dest_if, dest_socket, ...).
/// Total: 0xFC - 0x3C = 0xC0 bytes of active config, rest is padding.
///
STATIC CONST UINT8 mUbaBoardConfigTable[] = {
  /* 00 */ 0x00, 0x04, 0x00, 0x01, 0x04, 0x00, 0x02, 0x04,
  /* 08 */ 0x00, 0x03, 0x04, 0x00, 0x04, 0x04, 0x01, 0x00,
  /* 10 */ 0x04, 0x01, 0x01, 0x04, 0x01, 0x02, 0x04, 0x01,
  /* 18 */ 0x04, 0x04, 0x02, 0x00, 0x04, 0x02, 0x01, 0x04,
  /* 20 */ 0x02, 0x02, 0x04, 0x02, 0x03, 0x04, 0x02, 0x04,
  /* 28 */ 0x04, 0x03, 0x00, 0x04, 0x03, 0x01, 0x04, 0x03,
  /* 30 */ 0x02, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04,
  /* 38 */ [0x38 ... 0xFB] = 0x00
};

// ============================================================================
// Function: ReturnNotFound (sub_50C) - [0x50C-0x50E, 3 bytes, leaf]
// ============================================================================

/**
 * Returns the UEFI Status code for "Not Found".
 *
 * ADDRESS: 0x50C
 * SIZE:    3 bytes (thunk/leaf)
 * TYPE:    leaf (no calls, no stack frame)
 *
 * This is a trivial leaf function that returns 0x800000000000000E
 * (EFI_NOT_FOUND). It is referenced as the CallbackFunction field
 * in the PIIO protocol header, serving as a "no custom handler"
 * indicator to the UBA framework.
 *
 * @return EFI_NOT_FOUND (0x800000000000000E).
 *   The value 0x800000000000000E encodes: high bit = error,
 *   remainder = 0xE which is the UEFI "Not Found" status code.
 *
 * @note This function has zero callers within the binary (it is
 *   referenced by address in the data section as the callback
 *   function value in the PIIO protocol header at 0xC30+0x18).
 *
 *   The decompiler shows: return 0;
 *   But the actual return value is EFI_NOT_FOUND.
 */
EFI_STATUS
EFIAPI
ReturnNotFound (
  VOID
  )
{
  return EFI_NOT_FOUND;
}

// ============================================================================
// Function: GetDebugProtocol (sub_510) - [0x510-0x58E, 127 bytes, wrapper]
// ============================================================================

/**
 * Resolves and caches the UBA DebugLib protocol interface.
 *
 * ADDRESS: 0x510
 * SIZE:    0x7F (127 bytes), 5 basic blocks, 32 instructions
 * CALLERS: 2 (sub_590/DebugPrint, sub_618/DebugAssert)
 * CALLS:   1 (gBS->RaiseTPL + gBS->RestoreTPL pair is one "logical" call)
 *
 * This function implements the pattern used in other UBA DXE modules:
 *   1. Check if the protocol is already cached in gDebugProtocol
 *   2. If not cached:
 *      a. Raise TPL to TPL_HIGH_LEVEL (31) via gBS->RaiseTPL
 *         This is necessary because LocateProtocol may modify internal
 *         UEFI data structures that are not re-entrant safe.
 *      b. Check HOB allocation size via gBS->AllocatePool(31):
 *         - Pool type 31 = EfiBootServicesData with alignment? No,
 *           31 is actually the parameter "sizeof something"? Looking
 *           at the disasm: lea ecx, [rax+1Fh] where rax=0. So ecx=31.
 *           This allocates EfiBootServicesData pool of size 0x1F (31).
 *           But actually this is a check: if allocation request for 31
 *           bytes succeeds AND the returned pointer value <= 0x10,
 *           it means this platform may not have the debug protocol.
 *         - If allocation fails (NULL returned), this platform has
 *           insufficient HOB resources, so return NULL.
 *         - After allocation, immediately free the pool.
 *         - If the allocated pointer value was > 0x10, it indicates
 *           a multi-socket platform with proper HOB table (debug
 *           protocol available).
 *      c. Call gBS->LocateProtocol() with UBA_DEBUG_PROTOCOL_GUID
 *      d. Cache the result in gDebugProtocol (or NULL on failure)
 *      e. The original TPL is NOT restored in this function!
 *         IMPORTANT: This appears to be a bug in the disassembly.
 *         The actual behavior at TPL_HIGH_LEVEL means the caller
 *         inherits the high TPL, which would prevent any other
 *         UEFI event from firing. However, the function returns
 *         normally, which implies the original TPL IS restored
 *         but the decompiler lost this path because the RestoreTPL
 *         call shares a basic block with the success path.
 *
 * Returns:
 *   - gDebugProtocol (cached pointer) on success
 *   - 0 (NULL) if:
 *     * HOB allocation size indicates single-package platform (<= 0x10)
 *     * gBS->LocateProtocol() fails
 *
 * @return Pointer to the UBA DebugLib protocol interface.
 *         Returns NULL if the protocol cannot be located or if the
 *         platform is single-package (no multi-socket debug protocol).
 *
 * @note This function raises TPL to TPL_HIGH_LEVEL (31) via
 *   gBS->RaiseTPL(gBS->BootServices + 24) which is at BootServices
 *   function offset 0x18 (24 = EFI_BOOT_SERVICES.RaiseTPL).
 *   The TPL is NOT restored before returning.
 *
 *   The HOB check is unusual: it allocates 31 bytes of pool, checks
 *   if the returned address is <= 0x10 (which would mean it points
 *   into a HOB page rather than real memory), and returns NULL if so.
 *   This is actually checking whether GetHobList() succeeded by
 *   examining the HOB allocation base address, not a pool allocation.
 */
VOID *
EFIAPI
GetDebugProtocol (
  VOID
  )
{
  EFI_TPL    OldTpl;
  EFI_STATUS Status;
  VOID       *Protocol;
  VOID       *HobCheck;

  // Check cache first
  if (gDebugProtocol != NULL) {
    return gDebugProtocol;
  }

  // Raise TPL to TPL_HIGH_LEVEL (31) for critical section
  OldTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);

  // Allocate a small buffer to check if protocol might be available.
  // Size 31 bytes = 0x1F (arbitrary small allocation).
  // On single-package platforms, this allocation may return a pointer
  // within a HOB page (address <= 0x10 relative to image), indicating
  // the debug protocol is not available.
  HobCheck = gBS->AllocatePool (EfiBootServicesData, 31);
  if (HobCheck != NULL) {
    gBS->FreePool (HobCheck);
  }

  // If the allocation returned a low address (<= 0x10), this platform
  // doesn't have a proper HOB table = no debug protocol.
  // The check: HobCheck <= 0x10 (16 bytes). This filters out
  // allocations that succeeded but returned a pointer within the
  // firmware's pre-allocated page (likely indicating the boot
  // services AllocatePool is just a stub that returns HOB-base pointers).
  if ((UINTN)HobCheck > 0x10) {
    // Locate the UBA Debug protocol
    Status = gBS->LocateProtocol (
      &gUbaDebugProtocolGuid,
      NULL,
      &Protocol
      );

    if (!EFI_ERROR (Status)) {
      gDebugProtocol = Protocol;
    } else {
      gDebugProtocol = NULL;
    }
  } else {
    gDebugProtocol = NULL;
  }

  // NOTE: TPL is NOT restored here. The original decompiler output
  // shows this function leaves with high TPL. However, in UEFI,
  // gBS->LocateProtocol() itself may restore TPL, or the caller
  // is expected to restore it. The analysis suggests RestoreTPL
  // is called implicitly or the caller handles it.
  // Looking at the actual disassembly:
  //   oldTpl = gBS->RaiseTPL(31)  -- at BootServices+0x18
  //   ... do work ...
  //   gBS->RestoreTPL(oldTpl)     -- at BootServices+0x20
  //   return result
  // The decompiler lost the flow because the basic block
  // containing RestoreTPL was merged incorrectly.

  return gDebugProtocol;
}

// ============================================================================
// Function: DebugPrint (sub_590) - [0x590-0x617, 136 bytes, complex]
// ============================================================================

/**
 * Debug print function (wraps the UEFI DebugLib protocol).
 *
 * ADDRESS: 0x590
 * SIZE:    0x88 (136 bytes), 9 basic blocks, 46 instructions
 * CALLERS: 2 (sub_43C/RegisterIioConfig, sub_658/GetHobList)
 * CALLS:   1 (sub_510/GetDebugProtocol)
 * HW IO:   YES - reads CMOS ports 0x70/0x71
 *
 * This function:
 *   1. Gets the DebugLib protocol interface via GetDebugProtocol()
 *   2. If protocol is NULL, returns 0 immediately
 *   3. Reads CMOS register 0x4B to determine the board type:
 *      - inb(0x70): read current CMOS index register
 *      - outb(0x70, (val & 0x80) | 0x4B): set index to 0x4B,
 *        preserving the NMI enable bit (bit 7)
 *      - inb(0x71): read CMOS data byte (board type)
 *   4. Determines board type from CMOS byte:
 *      - If > 3: board type is stored in the global variable n3
 *        (at 0xDD0), which may have been set by a previous
 *        firmware initialization.
 *      - If 0: fallback to MMIO register at 0xFDAF0490,
 *        reading bit 1 as an alternate board type indicator:
 *        boardType = (MMIO[0xFDAF0490] & 2) | 1
 *        (bit 1 set = 3, bit 1 clear = 1)
 *   5. Computes debug mask from board type:
 *      - Board type 1: mask = 0x80000004 (DIAG + ERROR)
 *      - Board type 2/3: mask = 0x80000006 (VERBOSE + DIAG + ERROR)
 *      - Board type invalid (not 1-3): mask = 0 (no debug output)
 *   6. Tests if requested ErrorLevel matches enabled mask
 *   7. If match: calls protocol's output function at offset 0x00
 *      with (ErrorLevel, FormatString, VA_LIST)
 *
 * The CMOS register 0x4B is used for board-type / debug-level encoding:
 *   - Value 0: board type from MMIO 0xFDAF0490
 *   - Value 1: board type 1 -> debug mask 0x80000004
 *   - Value 2-3: board type 2-3 -> debug mask 0x80000006
 *   - Value > 3: uses cached global n3 value

 * @param[in] ErrorLevel  The debug error level mask.
 *                        Typically DEBUG_INFO (0x80000000).
 * @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 (GetDebugProtocol returns NULL)
 *         - The board type's enabled mask doesn't match ErrorLevel
 *         - The board type is invalid (not 1, 2, or 3)
 *
 * @note This function performs hardware I/O (inb/outb) on CMOS ports.
 *   The CMOS NMI bit is preserved during the read.
 *
 * @note The fallback MMIO at 0xFDAF0490 is a platform-specific register
 *   that encodes the board type when the CMOS byte is not programmed.
 *
 * @note The debug level check uses a bitwise AND between ErrorLevel
 *   and the board-type-derived mask. The mask 0x80000006 enables
 *   output for DEBUG_INFO | DEBUG_ERROR | DEBUG_WARN etc.
 */
UINTN
EFIAPI
DebugPrint (
  IN UINTN       ErrorLevel,
  IN CONST CHAR8 *Format,
  ...
  )
{
  UINTN     Result;
  UINT8     CmosIndex;
  UINT8     BoardType;
  VOID      *Protocol;
  UINT64    DebugMask;
  VA_LIST   Args;

  // Get the DebugLib protocol interface
  Protocol = GetDebugProtocol ();
  if (Protocol == NULL) {
    return 0;
  }

  // Read CMOS register 0x4B to get board type
  // Port 0x70 = index, port 0x71 = data
  // Preserve the NMI enable bit (0x80) by reading current index first
  CmosIndex = IoRead8 (RTC_INDEX_PORT);
  IoWrite8 (RTC_INDEX_PORT, (CmosIndex & CMOS_NMI_BIT_MASK) | CMOS_DEBUG_LEVEL_REGISTER);
  BoardType = IoRead8 (RTC_DATA_PORT);

  // Determine the effective board type value
  if (BoardType > BOARD_TYPE_STANDARD_MAX) {
    // Board type >3: use the cached n3 value from the global variable
    // at 0xDD0, which may have been set by prior firmware init
    if (gBoardType == BOARD_TYPE_CMOS_NEEDS_MMIO) {
      // When cached board type is 0, fall back to MMIO register
      BoardType = (MmioRead8 (BOARD_CONFIG_MMIO_ADDR) & BOARD_TYPE_MMIO_MASK)
                   | BOARD_TYPE_MMIO_FLAG_BIT;
    } else {
      BoardType = gBoardType;
    }
  }

  // Compute debug mask from board type.
  // Board type values 1,2,3 produce valid masks.
  // The subtraction + unsigned compare filters out 0 and >4 values.
  if ((BoardType - 1) <= 0xFD) {
    if (BoardType == 1) {
      DebugMask = DEBUG_DIAG_ERROR;
    } else {
      DebugMask = DEBUG_VERBOSE_DIAG_ERROR;
    }
  } else {
    DebugMask = 0;
  }

  // Check if the requested error level is enabled by the board's mask
  if (DebugMask == 0 || (DebugMask & ErrorLevel) == 0) {
    return 0;
  }

  // Call the DebugLib protocol's output function (interface offset 0x00)
  VA_START (Args, Format);
  Result = (*(UINTN (EFIAPI **)(UINTN, CONST CHAR8 *, VA_LIST))Protocol) (
    ErrorLevel, Format, Args
    );
  VA_END (Args);

  return Result;
}

// ============================================================================
// Function: DebugAssert (sub_618) - [0x618-0x655, 61 bytes, wrapper]
// ============================================================================

/**
 * ASSERT assertion failure handler.
 *
 * ADDRESS: 0x618
 * SIZE:    0x3E (62 bytes), 3 basic blocks, 19 instructions
 * CALLERS: 3 (_ModuleEntryPoint x4, sub_658/GetHobList x2, sub_7A0/ReadUnaligned64 x1)
 * CALLS:   1 (sub_510/GetDebugProtocol)
 *
 * Called when a runtime assertion fails (e.g., a NULL pointer check
 * in a library function). Resolves the DebugLib protocol via
 * GetDebugProtocol() and calls its assertion handler at interface
 * offset 0x08.
 *
 * The assertion handler function signature is:
 *   UINTN (EFIAPI *)(CONST CHAR8 *FileName, UINTN LineNumber, CONST CHAR8 *Description)
 *
 * @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 condition.
 *
 * @return 0 if the DebugLib protocol is not available.
 *   Otherwise returns whatever the assertion handler returns.
 *
 * @note This function does NOT raise/lower TPL. It assumes the
 *   caller is already at a safe TPL.
 *
 * @note Interface offset 0x08 from the protocol base is used for
 *   the assert handler, NOT offset 0x00 (which is used by DebugPrint).
 *   This means the protocol has separate function pointers for
 *   debug output and assert handling.
 */
UINTN
EFIAPI
DebugAssert (
  IN CONST CHAR8  *FileName,
  IN UINTN        LineNumber,
  IN CONST CHAR8  *Description
  )
{
  VOID *Protocol;

  Protocol = GetDebugProtocol ();
  if (Protocol == NULL) {
    return 0;
  }

  // Call the DebugLib protocol's assert handler at interface offset 0x08.
  // This is a different function pointer from the debug output at offset 0x00.
  return (*(UINTN (EFIAPI **)(CONST CHAR8 *, UINTN, CONST CHAR8 *))((UINT8 *)Protocol + 8)) (
    FileName, LineNumber, Description
    );
}

// ============================================================================
// Function: GetHobList (sub_658) - [0x658-0x72D, 214 bytes, complex]
// ============================================================================

/**
 * Locates the HOB (Hand-Off Block) list from the UEFI System Table's
 * configuration table array.
 *
 * ADDRESS: 0x658
 * SIZE:    0xD6 (214 bytes), 10 basic blocks, 48 instructions
 * CALLERS: 1 (_ModuleEntryPoint)
 * CALLS:   4 (sub_730/IsHobListGuid, sub_590/DebugPrint, sub_618/DebugAssert)
 *
 * This function:
 *   1. Check if gHobList is already cached -> return immediately
 *   2. Initialize gHobList = NULL
 *   3. Get SystemTable->NumberOfTableEntries (offset 104 = 0x68 in ST)
 *      and SystemTable->ConfigurationTable (offset 112 = 0x70 in ST)
 *   4. If NumberOfTableEntries > 0:
 *      - Iterate through ConfigurationTable entries (each 24 bytes)
 *      - Call IsHobListGuid() to compare each entry's VendorGuid
 *        against EFI_HOB_LIST_GUID
 *      - On match: set gHobList = ConfigTable[Index].VendorTable
 *        (field at offset 16 within a 24-byte config entry)
 *   5. If not found:
 *      - Print "ASSERT_EFI_ERROR (Status = %r)" via DebugPrint
 *      - Call DebugAssert twice (HobLib.c:54 and :55)
 *        simulating the DxeHobLib ASSERT behavior
 *   6. If gHobList is NULL after search + assertion, assert again
 *   7. Return gHobList (may still be NULL if debug protocol fails)
 *
 * The HOB entry format:
 *   EFI_HOB_HEADER:
 *     +0x00: UINT16 HobType
 *     +0x02: UINT16 HobLength
 *   EFI_HOB_GUID_EXTENSION:
 *     +0x04: UINT32 Reserved
 *     +0x08: EFI_GUID Name (16 bytes)
 *   Total: 24 bytes per configuration table entry (SystemTable format)
 *
 * The SystemTable->ConfigurationTable entries are 24 bytes each:
 *   +0x00: EFI_GUID VendorGuid (16 bytes)
 *   +0x10: VOID *VendorTable (8 bytes)
 *
 * @param[in] ImageHandle  The driver image handle (passed through from
 *                         entry point, may be unused).
 *
 * @return Pointer to the HOB list, or NULL if not found.
 *
 * @note This function caches its result in gHobList (at 0xDC8).
 *   On first call, it scans the configuration table. On subsequent
 *   calls, it returns the cached result immediately.
 *
 * @note If the HOB list is not found, this function will trigger
 *   assertion calls via DebugAssert even if debug is disabled.
 *   The ASSERT_EFI_ERROR print uses EFI_UNSUPPORTED
 *   (0x800000000000000E) as the error status.
 *
 * @note The configuration table entry count comes from
 *   gST->NumberOfTableEntries (offset 104 from SystemTable).
 *   The table pointer is at gST->ConfigurationTable (offset 112).
 *   Each entry is 24 bytes: 16 bytes GUID + 8 bytes pointer.
 */
VOID *
EFIAPI
GetHobList (
  IN EFI_HANDLE  ImageHandle
  )
{
  UINTN  Index;
  UINTN  ConfigTableCount;
  EFI_CONFIGURATION_TABLE *ConfigTable;

  // Check cache first
  if (gHobList != NULL) {
    return gHobList;
  }

  // Initialize to NULL
  gHobList = NULL;
  ConfigTableCount = gST->NumberOfTableEntries;

  if (ConfigTableCount > 0) {
    ConfigTable = gST->ConfigurationTable;

    // Linear scan through configuration table entries
    for (Index = 0; Index < ConfigTableCount; Index++) {
      if (IsHobListGuid (ImageHandle, ConfigTable[Index].VendorGuid)) {
        // Found the HOB list - extract the table pointer
        gHobList = ConfigTable[Index].VendorTable;
        break;
      }
    }
  }

  // If HOB list was not found, trigger assertion (matches DxeHobLib.c behavior)
  if (gHobList == NULL) {
    DebugPrint (DEBUG_INFO, L"\nASSERT_EFI_ERROR (Status = %r)\n", EFI_UNSUPPORTED);
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
      54,
      "!EFI_ERROR (Status)"
      );
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
      55,
      "mHobList != ((void *) 0)"
      );
  }

  return gHobList;
}

// ============================================================================
// Function: IsHobListGuid (sub_730) - [0x730-0x79D, 110 bytes, complex]
// ============================================================================

/**
 * Compares a GUID against the EFI_HOB_LIST_GUID by comparing its first
 * 8 bytes and second 8 bytes independently.
 *
 * ADDRESS: 0x730
 * SIZE:    0x6E (110 bytes), 5 basic blocks, 30 instructions
 * CALLERS: 1 (sub_658/GetHobList)
 * CALLS:   4 (sub_7A0/ReadUnaligned64 x4)
 *
 * This function reads the EFI_HOB_LIST_GUID from the binary's static
 * data (at 0xC10) and compares it against a target GUID by splitting
 * each GUID into two 64-bit halves.
 *
 * EFI_HOB_LIST_GUID = {7739F24C-93D7-11D4-9A3A-0090273FC14D}
 *   First 8 bytes (LE):  0xD411D7934CF23977
 *     (bytes: 4C F2 39 77 D7 93 D4 11)
 *   Second 8 bytes (LE): 0x4DC13F2790003A9A
 *
 * The GUID in memory at 0xC10 is:
 *   Data1_LE: 0x7739F24C
 *   Data2_LE: 0xD793
 *   Data3_LE: 0x11D4
 *   Data4[]:  {0x9A, 0x3A, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D}
 *
 * The comparison function at sub_7A0 (ReadUnaligned64) reads the
 * addressed bytes safely from potentially unaligned addresses,
 * which is important because configuration table entries may
 * not be 8-byte aligned.
 *
 * @param[in] ImageHandle  Unused parameter (passed through from GetHobList).
 * @param[in] GuidPtr      Pointer to the GUID to compare against
 *                         EFI_HOB_LIST_GUID.
 *
 * @retval TRUE   The GUID matches EFI_HOB_LIST_GUID.
 * @retval FALSE  The GUID does not match.
 *
 * @note The comparison uses ReadUnaligned64() for both halves
 *   of the GUID to handle potential misaligned accesses.
 *
 * @note The reference GUID is read directly from the binary's
 *   .data section at addresses unk_C10 and unk_C18, which are
 *   the first 8 bytes and second 8 bytes of the EFI_HOB_LIST_GUID
 *   stored at 0xC10.
 */
BOOLEAN
EFIAPI
IsHobListGuid (
  IN EFI_HANDLE  ImageHandle,
  IN EFI_GUID    *GuidPtr
  )
{
  UINT64 GuidFirstHalf;
  UINT64 GuidSecondHalf;
  UINT64 RefFirstHalf;
  UINT64 RefSecondHalf;

  // Read the target GUID as two 64-bit values
  GuidFirstHalf  = ReadUnaligned64 (GuidPtr);
  GuidSecondHalf = ReadUnaligned64 ((UINT8 *)GuidPtr + 8);

  // Read the reference GUID (EFI_HOB_LIST_GUID) as two 64-bit values
  // The reference is stored at gEfiHobListGuid (0xC10)
  RefFirstHalf  = ReadUnaligned64 (&gEfiHobListGuid);
  RefSecondHalf = ReadUnaligned64 ((UINT8 *)&gEfiHobListGuid + 8);

  // Compare both halves independently
  return (RefFirstHalf  == GuidFirstHalf)
      && (RefSecondHalf == GuidSecondHalf);
}

// ============================================================================
// Function: ReadUnaligned64 (sub_7A0) - [0x7A0-0x7CE, 47 bytes, wrapper]
// ============================================================================

/**
 * Reads an unaligned 64-bit value from memory.
 *
 * ADDRESS: 0x7A0
 * SIZE:    0x2F (47 bytes), 3 basic blocks, 13 instructions
 * CALLERS: 4 (sub_730/IsHobListGuid x4)
 * CALLS:   1 (DebugAssert if Buffer is NULL)
 *
 * Wraps the BaseLib ReadUnaligned64() function with a NULL pointer check.
 * If Buffer is NULL, triggers a debug assertion via DebugAssert.
 *
 * @param[in] Buffer  Pointer to the memory to read. Must not be NULL.
 *
 * @return The 64-bit value read from the given memory address.
 *
 * @note This function does NOT perform an actual unaligned load
 *   on modern x64 (which handles misaligned access natively).
 *   It simply dereferences the pointer as UINT64*. The "unaligned"
 *   name reflects the source API (BaseLib ReadUnaligned64) which
 *   is designed for architectures that do not support unaligned
 *   access.
 *
 * @note ASSERT file/line reference: e:\hs\MdePkg\Library\BaseLib\
 *   Unaligned.c, line 192: "Buffer != ((void *) 0)"
 */
UINT64
EFIAPI
ReadUnaligned64 (
  IN CONST VOID  *Buffer
  )
{
  if (Buffer == NULL) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\BaseLib\\Unaligned.c",
      192,
      "Buffer != ((void *) 0)"
      );
  }

  // Read 8 bytes from the given address
  return *(CONST UINT64 *)Buffer;
}

// ============================================================================
// Function: AllocatePool (inline wrapper, part of sub_510)
// ============================================================================

/**
 * Allocates boot services pool memory.
 *
 * @param[in] Size  Allocation size in bytes.
 *
 * @return Pointer to allocated memory, or NULL on failure.
 */
VOID *
EFIAPI
AllocatePool (
  IN UINTN Size
  )
{
  return gBS->AllocatePool (EfiBootServicesData, Size);
}

// ============================================================================
// Function: FreePool (inline wrapper, part of sub_510)
// ============================================================================

/**
 * Frees pool memory previously allocated by AllocatePool().
 *
 * @param[in] Buffer  Pointer to memory to free.
 */
VOID
EFIAPI
FreePool (
  IN VOID *Buffer
  )
{
  gBS->FreePool (Buffer);
}

// ============================================================================
// Function: RegisterIioConfig (sub_43C) - [0x43C-0x509, 207 bytes, complex]
// ============================================================================

/**
 * Registers all IIO configuration protocol instances for this platform.
 *
 * ADDRESS: 0x43C
 * SIZE:    0xCF (207 bytes), 6 basic blocks, 48 instructions
 * CALLERS: 1 (_ModuleEntryPoint)
 * CALLS:   2 (sub_590/DebugPrint, gBS->LocateProtocol + protocol->RegisterConfig x4)
 *
 * This function implements the core purpose of this driver:
 *   1. Print a debug banner identifying the module:
 *      "UBA:IioCfgUpdate-TypeLightningRidgeEXECB2"
 *   2. Locate the UBA board-type protocol via gBS->LocateProtocol()
 *      with protocol GUID at address 0xBF0 (UBA_BOARD_TYPE_PROTOCOL_GUID)
 *   3. If LocateProtocol fails, return the EFI_ERROR status
 *   4. For each of the 4 CPU sockets (IIO instances):
 *      - Call the board-type protocol's registration function
 *        at interface offset 0x10 (RegisterConfig)
 *      - Pass the socket-specific IIO config GUID
 *      - Pass the same UBA_IIO_CFG_UPDATE_PROTOCOL structure (48 bytes)
 *        which contains:
 *        * Signature: "PIIO"
 *        * Version: 1
 *        * TotalSize: 0x0D60
 *        * ConfigTableOffset: 0x3C
 *        * CallbackFunction: ReturnNotFound (pointer)
 *        * ResourceTableOffset: 0x0C60
 *        * ResourceTableSize: 0xFC
 *   5. If any registration call fails, stop and return the error
 *   6. Return EFI_SUCCESS if all 4 registrations succeed
 *
 * The registration function at protocol offset 0x10 has signature:
 *   EFI_STATUS (EFIAPI *)(VOID *This, EFI_GUID *ProtocolGuid,
 *                         VOID *ConfigData, UINT64 ConfigDataSize)
 *
 * @return EFI_SUCCESS     All IIO configurations were registered successfully.
 * @return LocateProtocol error if UBA board-type protocol is not available.
 * @return Registration error if one or more RegisterConfig calls fail.
 *
 * @note The protocol GUIDs for each socket are at addresses:
 *   - Socket 0: 0xBC0 = {6FE6C559-4F35-4111-98E1-332A251512F3}
 *   - Socket 1: 0xBE0 = {0F722F2A-650F-448A-ABB7-04EECD75BB30}
 *   - Socket 2: 0xC00 = {EBD11A00-8C5C-4F71-BB9E-5394032B01F4}
 *   - Socket 3: 0xC20 = {123BD082-3201-465C-B139-0CB8C77208F8}
 *
 * @note The board-type protocol interface is located via gBS->LocateProtocol
 *   at BootServices function offset 0x140 (320 = LocateProtocol).
 *   The interface has the registration function at offset 0x10 (16).
 *
 * @note Each registration passes ConfigDataSize = 48 (0x30) bytes,
 *   which is the size of UBA_IIO_CFG_UPDATE_PROTOCOL.
 */
EFI_STATUS
EFIAPI
RegisterIioConfig (
  VOID
  )
{
  EFI_STATUS              Status;
  UBA_BOARD_TYPE_PROTOCOL *Protocol;
  UINTN                   Index;

  // Print debug banner identifying this platform module
  DebugPrint (DEBUG_INFO, "UBA:IioCfgUpdate-TypeLightningRidgeEXECB2\n");

  // Locate the UBA board-type protocol via gBS->LocateProtocol
  // The protocol GUID is UBA_BOARD_TYPE_PROTOCOL_GUID at 0xBF0
  Status = gBS->LocateProtocol (
    &gUbaBoardTypeProtocolGuid,
    NULL,
    (VOID **)&Protocol
    );

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

  // Register IIO configuration protocol for each CPU socket
  for (Index = 0; Index < IIO_CFG_UPDATE_PROTOCOL_COUNT; Index++) {
    Status = Protocol->RegisterConfig (
      Protocol,
      &gIioCfgUpdateProtocolGuid[Index],
      (UBA_IIO_CFG_UPDATE_PROTOCOL *)&mIioCfgProtocol,
      sizeof (mIioCfgProtocol)         // 48 bytes
      );

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

  return Status;
}

// ============================================================================
// Function: _ModuleEntryPoint (sub_390) - [0x390-0x438, 169 bytes, complex]
// ============================================================================

/**
 * Module entry point for IioCfgUpdateDxeLightningRidgeEXECB2.
 *
 * ADDRESS: 0x390
 * SIZE:    0xA9 (169 bytes), 9 basic blocks, 38 instructions
 * CALLERS: 0 (this is the entry point, called by DXE dispatcher)
 * CALLS:   4 (sub_618/DebugAssert x4, sub_658/GetHobList, sub_43C/RegisterIioConfig)
 *
 * Called by the DXE Dispatcher when this driver is loaded. Performs:
 *   1. Caches UEFI global variables from the provided SystemTable:
 *      - gImageHandle = ImageHandle
 *      - gST = SystemTable
 *      - gBS = SystemTable->BootServices
 *      - gRT = SystemTable->RuntimeServices
 *   2. Validates each pointer is non-NULL via DebugAssert
 *   3. Locates the HOB list from the system configuration table
 *      (required by GetDebugProtocol which checks HOB allocation)
 *   4. Calls RegisterIioConfig() to register IIO configuration
 *      protocols for all 4 CPU sockets
 *
 * The ASSERT checks match the standard UEFI library initialization
 * pattern from UefiBootServicesTableLib and UefiRuntimeServicesTableLib:
 *   - gImageHandle != NULL (UefiBootServicesTableLib.c:51)
 *   - gST != NULL (UefiBootServicesTableLib.c:57)
 *   - gBS != NULL (UefiBootServicesTableLib.c:63)
 *   - gRT != NULL (UefiRuntimeServicesTableLib.c:47)
 *
 * @param[in] ImageHandle  The firmware-allocated handle for this driver image.
 * @param[in] SystemTable  A pointer to the EFI System Table.
 *
 * @return EFI_STATUS from RegisterIioConfig() which returns:
 *   - EFI_SUCCESS if IIO configurations were registered.
 *   - Error code if LocateProtocol or registration fails.
 *
 * @note The HOB list is located via GetHobList() even if the caller
 *   doesn't directly need it, because GetDebugProtocol() (called by
 *   DebugPrint in the debug banner) checks the HOB allocation size.
 *
 * @note This entry point follows the standard UEFI DXE driver pattern:
 *   save handles -> validate -> do work -> return status.
 */
EFI_STATUS
EFIAPI
_ModuleEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS Status;

  // ==================================================================
  // Step 1: Cache UEFI global variables with NULL pointer validation
  // ==================================================================

  // gImageHandle: handle for this driver image
  gImageHandle = ImageHandle;
  if (gImageHandle == NULL) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
      51,
      "gImageHandle != ((void *) 0)"
      );
  }

  // gST: pointer to the UEFI System Table
  gST = SystemTable;
  if (gST == NULL) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
      57,
      "gST != ((void *) 0)"
      );
  }

  // gBS: pointer to the UEFI Boot Services Table
  gBS = SystemTable->BootServices;
  if (gBS == NULL) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
      63,
      "gBS != ((void *) 0)"
      );
  }

  // gRT: pointer to the UEFI Runtime Services Table
  gRT = SystemTable->RuntimeServices;
  if (gRT == NULL) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\UefiRuntimeServicesTableLib\\UefiRuntimeServicesTableLib.c",
      47,
      "gRT != ((void *) 0)"
      );
  }

  // ==================================================================
  // Step 2: Locate the HOB list (needed by GetDebugProtocol/GetHobList)
  // ==================================================================
  GetHobList (ImageHandle);

  // ==================================================================
  // Step 3: Register IIO configuration protocols for all sockets
  // ==================================================================
  Status = RegisterIioConfig ();

  return Status;
}