Newer
Older
AMI-Aptio-BIOS-Reversed / IioCfgUpdateDxeLightningRidgeEXECB1 / IioCfgUpdateDxeLightningRidgeEXECB1.c
@Ajax Dong Ajax Dong 2 days ago 22 KB Init
/**
 * @file IioCfgUpdateDxeLightningRidgeEXECB1.c
 *
 * @brief IioCfgUpdateDxeLightningRidgeEXECB1 - UEFI DXE driver for registering
 *        IIO configuration data on the LightningRidgeEXECB1 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\
 *   TypeLightningRidgeEXECB1\IioCfgUpdateDxe\IioCfgUpdateDxe\
 *   DEBUG\IioCfgUpdateDxeLightningRidgeEXECB1.pdb
 *
 * BOARD ID: 0x0D60 (LightningRidgeEXECB1)
 * IIO CFG TABLE SIZE: 0x50C (1292 bytes, 21 entries of 12 bytes + padding)
 *
 * @see IioCfgUpdateDxeLightningRidgeEXECB1.h for type definitions and GUIDs.
 */

#include "IioCfgUpdateDxeLightningRidgeEXECB1.h"

// ============================================================================
// Module Globals
// ============================================================================

///
/// Cached debug protocol interface (initialized on first use).
///
VOID *gDebugProtocol = NULL;

///
/// Cached HOB list pointer (initialized on first use).
///
VOID *gHobList = NULL;

// ============================================================================
// GUID Definitions (instances)
// ============================================================================

///
/// EFI_HOB_LIST_GUID instance - used to locate HOB list in configuration table.
///
EFI_GUID gEfiHobListGuid = EFI_HOB_LIST_GUID;

///
/// UBA Board-Type Protocol GUID - interface to register configuration data.
///
EFI_GUID gUbaBoardTypeProtocolGuid = UBA_BOARD_TYPE_PROTOCOL_GUID;

///
/// UBA IIO Config Update protocol GUIDs (one per CPU socket).
///
static EFI_GUID gIioCfgUpdateProtocolGuid[IIO_CFG_UPDATE_PROTOCOL_COUNT] = {
  UBA_IIO_CFG_UPDATE_PROTOCOL_GUID_0,  ///< Socket 0
  UBA_IIO_CFG_UPDATE_PROTOCOL_GUID_1,  ///< Socket 1
  UBA_IIO_CFG_UPDATE_PROTOCOL_GUID_2,  ///< Socket 2
  UBA_IIO_CFG_UPDATE_PROTOCOL_GUID_3   ///< Socket 3
};

///
/// UBA Debug Protocol GUID - protocol for debug output.
///
EFI_GUID gUbaDebugProtocolGuid = UBA_DEBUG_PROTOCOL_GUID;

// ============================================================================
// IIO Configuration Table
// ============================================================================

///
/// UBA_IIO_CFG_UPDATE_PROTOCOL - The IIO configuration protocol structure
/// that is registered for each CPU socket on this platform.
///
/// This structure describes the IIO configuration table layout:
///   - Signature: "PIIO"
///   - BoardId:   0x0D60
///   - SlotNumber: 0x3C (60)
///   - IioCfgTableSize: 0x50C (1292 bytes)
///   - IioCfgTable: offset to IioCfgTable[] below
///   - Reserved1: 0xFC
///
STATIC CONST UBA_IIO_CFG_UPDATE_PROTOCOL mIioCfgProtocol = {
  UBA_IIO_CFG_UPDATE_SIGNATURE,          ///< Signature "PIIO"
  LIGHTNINGRIDGE_EXECB1_BOARD_ID,         ///< BoardId = 0x0D60
  0,                                       ///< Reserved0 (padding)
  LIGHTNINGRIDGE_EXECB1_SLOT_NUMBER,      ///< SlotNumber = 0x3C
  LIGHTNINGRIDGE_EXECB1_IIO_CFG_SIZE,     ///< IioCfgTableSize = 0x50C
  (UINT64)(UINTN)&mIioCfgTable[0],        ///< IioCfgTable pointer
  0xFC                                     ///< Reserved1
};

///
/// IIO configuration table for LightningRidgeEXECB1.
///
/// This table contains 21 entries of 12 bytes each (252 bytes total),
/// with the remaining space (0x50C - 252 = 1040 bytes) being padding/reserved.
///
/// Each entry configures an IIO register/port for a specific socket function.
///
/// Entry format (UBA_IIO_CFG_UPDATE_TABLE_ENTRY):
///   Byte 0: Type (function/register selector)
///   Byte 1: SubType (sub-function or qualifier)
///   Bytes 2-3: Reserved
///   Bytes 4-7: Reserved
///   Byte 8: Value (target data)
///   Byte 9: Port (target port/address)
///   Bytes 10-11: Reserved
///
/// The 0xFF bytes in the reserved fields indicate "don't care" / unchecked.
/// The board config protocol performs the actual IIO register programming
/// based on these entries.
///
STATIC CONST UINT8 mIioCfgTable[0x50C] = {
  // ---- Entry  0: UPT (0x01) PCIE (0x4C) Slot=01, Port=00 ----
  0x01, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x01, 0x00, 0x4C, 0x01,
  // ---- Entry  1: UPT (0x02) PCIE (0x4C) Slot=01, Port=01 ----
  0x02, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x01, 0x01, 0x4C, 0x01,
  // ---- Entry  2: UPT (0x03) PCIE (0x4E) Slot=01, Port=00 ----
  0x03, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x01, 0x00, 0x4E, 0x01,
  // ---- Entry  3: UPT (0x04) PCIE (0x4E) Slot=01, Port=01 ----
  0x04, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x01, 0x01, 0x4E, 0x01,
  // ---- Entry  4: UPT (0x05) None/Disabled ----
  0x05, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
  // ---- Entry  5: UPT (0x09) PCIE (0x40) ----
  0x09, 0x01, 0x01, 0xFF, 0xFF, 0x01, 0x00, 0x40, 0x00, 0xFF, 0xFF, 0x00,
  // ---- Entry  6: UPT (0x15) None/Disabled ----
  0x15, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x01,
  // ---- Entry  7: UPT (0x16) ----
  0x16, 0x05, 0x01, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
  // ---- Entry  8: UPT (0x1A) ----
  0x1A, 0x07, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
  // ---- Entry  9: UPT (0x1E) ----
  0x1E, 0x0A, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x01, 0x00, 0x40, 0x00,
  // ---- Entry 10: UPT (0x1F) PCIE (0x40) ----
  0x1F, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x01, 0x01, 0x40, 0x01,
  // ---- Entry 11: UPT (0x20) PCIE (0x42) ----
  0x20, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x01, 0x00, 0x42, 0x01,
  // ---- Entry 12: UPT (0x21) PCIE (0x42) ----
  0x21, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x01, 0x01, 0x42, 0x01,
  // ---- Entry 13: UPT (0x2A) ----
  0x2A, 0x0E, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
  // ---- Entry 14: UPT (0x2B) ----
  0x2B, 0x06, 0x01, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
  // ---- Entry 15: UPT (0x2F) ----
  0x2F, 0x0F, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
  // ---- Entry 16: UPT (0x33) ----
  0x33, 0x08, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
  // ---- Entry 17: UPT (0x3F) ----
  0x3F, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x01,
  // ---- Entry 18: UPT (0x40) ----
  0x40, 0x13, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
  // ---- Entry 19: UPT (0x44) ----
  0x44, 0x04, 0x01, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
  // ---- Entry 20: UPT (0x48) ----
  0x48, 0x02, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
  // ---- Padding to fill 0x50C bytes ----
  [0x0FC ... 0x50B] = 0x00
};

// ============================================================================
// Static Data: UBA Board Config Table
// ============================================================================

///
/// UBA board configuration lookup table at offset 0xD60.
///
/// This small table provides a mapping between UBA protocol indices and
/// board configuration data. Format is 12 bytes per entry.
///
/// For LightningRidgeEXECB1, this table contains 20 entries mapping
/// protocol-to-protocol connections for the 4 IIO sockets.
///
STATIC CONST UINT8 mUbaBoardConfigTable[] = {
  /* 00 */ 0x00, 0x04, 0x00, 0x01, 0x04, 0x00, 0x02, 0x04, 0x00, 0x03, 0x04, 0x00,
  /* 04 */ 0x04, 0x04, 0x01, 0x00, 0x04, 0x01, 0x01, 0x04, 0x01, 0x02, 0x04, 0x01,
  /* 03 */ 0x04, 0x01, 0x04, 0x04, 0x02, 0x00, 0x04, 0x02, 0x01, 0x04, 0x02, 0x02,
  /* 04 */ 0x03, 0x04, 0x02, 0x04, 0x04, 0x03, 0x00, 0x04, 0x03, 0x01, 0x04, 0x03,
  /* 02 */ 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04,
  [0x3C ... 0x3F] = 0x00
};

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

/**
 * Returns the UEFI status code for "Not Found".
 *
 * Trivial stub function at address 0x50C that returns EFI_NOT_FOUND.
 * May be used as a fallback handler or placeholder by the UBA framework.
 *
 * @return EFI_NOT_FOUND (0x800000000000000E).
 */
EFI_STATUS
ReturnNotFound (
  VOID
  )
{
  return EFI_NOT_FOUND;
}

/**
 * Resolves and caches the UBA DebugLib protocol interface.
 *
 * This function implements the pattern found in other UBA DXE modules:
 *   1. Check if the protocol is already cached in gDebugProtocol
 *   2. If not cached, raise TPL to TPL_HIGH_LEVEL for atomic access
 *   3. Call gBS->LocateProtocol() with UBA_DEBUG_PROTOCOL_GUID
 *   4. Restore previous TPL
 *   5. Cache and return the result
 *
 * The HOB allocation size check (>= 0x10) filters out single-package
 * platforms that don't have the debug protocol.
 *
 * @return Pointer to the UBA DebugLib protocol interface.
 *         Returns NULL if the protocol could not be located.
 */
VOID *
GetDebugProtocol (
  VOID
  )
{
  EFI_TPL    OldTpl;
  EFI_STATUS Status;
  VOID       *Protocol;

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

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

  // Check if HOB allocation size indicates a multi-package platform
  // (HOB with size > 0x10 means there are resources for protocol lookup)
  if (gHobList != NULL && *(UINT16 *)((UINT8 *)gHobList + 4) > 0x10) {
    // Restore TPL and return NULL - protocol not available on this platform
    gBS->RestoreTPL (OldTpl);
    return NULL;
  }

  // Locate the UBA Debug protocol
  Status = gBS->LocateProtocol (
    &gUbaDebugProtocolGuid,  ///< Protocol GUID
    NULL,                     ///< No registration handle
    &Protocol                 ///< Returned protocol interface
    );

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

  // Restore TPL
  gBS->RestoreTPL (OldTpl);

  return gDebugProtocol;
}

/**
 * Debug print function (wraps the UEFI DebugLib protocol).
 *
 * Checks the CMOS board-type register to determine if debug output at the
 * given severity level should be produced. If so, calls the DebugLib protocol's
 * output function at interface offset 0x08.
 *
 * The CMOS register 0x4B encodes the board type:
 *   - 0  : Board type is derived from MMIO register 0xFDAF0490
 *   - 1-3: Direct board type values (standard types)
 *   - >3 : Non-standard type, used as-is
 *
 * The debug level mask 0x80000000 (DEBUG_INFO) is compared against the
 * board type's configured debug mask to determine if output is enabled.
 *
 * @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.
 */
UINTN
EFIAPI
DebugPrint (
  IN UINTN       ErrorLevel,
  IN CONST CHAR8 *Format,
  ...
  )
{
  UINTN                          Result;
  UINT8                          BoardType;
  UINT8                          BoardTypeValue;
  UINT64                         DebugProtocol;
  UINT64                         DebugMask;
  VA_LIST                        VaList;
  UBA_BOARD_TYPE_PROTOCOL        *Protocol;

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

  // Read CMOS register 0x4B to get board type
  // Port 0x70 = index, port 0x71 = data
  BoardType = IoRead8 (RTC_INDEX_PORT);
  IoWrite8 (RTC_INDEX_PORT, (BoardType & 0x80) | CMOS_DEBUG_LEVEL_REGISTER);
  BoardTypeValue = IoRead8 (RTC_DATA_PORT);

  // Determine the board type value
  if (BoardTypeValue <= BOARD_TYPE_STANDARD_MAX) {
    if (BoardTypeValue == BOARD_TYPE_CMOS_NEEDS_MMIO) {
      // When CMOS byte is 0, read from MMIO register and derive board type
      BoardTypeValue = (MmioRead8 (BOARD_CONFIG_MMIO_ADDR) & BOARD_TYPE_MMIO_MASK)
                        | BOARD_TYPE_MMIO_FLAG_BIT;
    }
  }

  // Board type 0 is reserved/invalid (subtract 1 to get index)
  // Valid board types are 1, 2, 3 -> indices 0, 1, 2
  if ((BoardTypeValue - 1) <= 0xFD) {
    // Determine debug mask based on board type index
    if (BoardTypeValue == 1) {
      DebugMask = 0x80000004;  ///< Board type 1: DIAG+ERROR mask
    } else {
      DebugMask = 0x80000006;  ///< Board types 2, 3: VERBOSE+DIAG+ERROR mask
    }
  } else {
    DebugMask = 0;
  }

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

  // Call the DebugLib protocol's output function (offset 0x08)
  VA_START (VaList, Format);
  Result = (*(UINTN (EFIAPI **)(UINTN, CONST CHAR8 *, VA_LIST))DebugProtocol) (
    ErrorLevel, Format, VaList
    );
  VA_END (VaList);

  return Result;
}

/**
 * ASSERT assertion failure handler.
 *
 * 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 failure handler at interface offset 0x08.
 *
 * @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.
 */
UINTN
DebugAssert (
  IN CONST CHAR8  *FileName,
  IN UINTN        LineNumber,
  IN CONST CHAR8  *Description
  )
{
  UINT64 DebugProtocol;

  DebugProtocol = (UINTN)GetDebugProtocol ();
  if (DebugProtocol == 0) {
    return 0;
  }

  // Call the DebugLib protocol's assert handler (offset 0x08)
  // The assert handler takes (FileName, LineNumber, Description)
  return (*(UINTN (EFIAPI **)(CONST CHAR8 *, UINTN, CONST CHAR8 *))DebugProtocol) (
    FileName, LineNumber, Description
    );
}

/**
 * Compares a GUID against the EFI_HOB_LIST_GUID by comparing its first 8 bytes
 * and second 8 bytes independently.
 *
 * Uses ReadUnaligned64() to safely read potentially unaligned GUID pointers.
 *
 * @param[in] ImageHandle  Unused parameter.
 * @param[in] GuidPtr      Pointer to the GUID to compare.
 *
 * @retval TRUE   The GUID matches EFI_HOB_LIST_GUID.
 * @retval FALSE  The GUID does not match.
 */
BOOLEAN
IsHobListGuid (
  IN EFI_HANDLE  ImageHandle,
  IN EFI_GUID    *GuidPtr
  )
{
  UINT64 GuidFirstHalf;
  UINT64 GuidSecondHalf;

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

  // Compare against EFI_HOB_LIST_GUID (7739F24C-93D7-11D4-9A3A-0090273FC14D)
  // First 8 bytes: 0xD411D7934CF23977 (little-endian: 7739F24C-93D7-11D4)
  // Second 8 bytes: 0x4DC13F2790003A9A (little-endian: 9A3A-0090273FC14D)
  return (GuidFirstHalf == 0xD411D7934CF23977ULL)
      && (GuidSecondHalf == 0x4DC13F2790003A9AULL);
}

/**
 * Reads an unaligned 64-bit value from memory.
 *
 * Wraps the BaseLib ReadUnaligned64() function with a NULL pointer check.
 * Triggers an ASSERT if called with a NULL buffer.
 *
 * @param[in] Buffer  Pointer to the memory to read. Must not be NULL.
 *
 * @return The 64-bit value read from the given address.
 */
UINT64
ReadUnaligned64 (
  IN CONST VOID  *Buffer
  )
{
  if (Buffer == NULL) {
    // Trigger debug assertion via DebugLib
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\BaseLib\\Unaligned.c",
      192,
      "Buffer != ((void *) 0)"
      );
  }

  // Read 8 bytes from potentially unaligned address
  return *(UINT64 *)Buffer;  ///< Simple 64-bit read (BaseLib handles unaligned access)
}

/**
 * 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 (7739f24c-93d7-11d4-9a3a-0090273fc14d).
 * The comparison is done by matching the first 8 bytes and second 8 bytes of
 * the GUID separately using IsHobListGuid().
 *
 * Results are cached in the global variable gHobList to avoid re-scanning.
 *
 * If the HOB list is not found, triggers an ASSERT_EFI_ERROR with
 * status EFI_UNSUPPORTED (0x800000000000000E).
 *
 * @param[in] ImageHandle  The driver image handle (passed through from entry,
 *                         may be unused).
 *
 * @return Pointer to the HOB list, or NULL if not found.
 */
VOID *
GetHobList (
  IN EFI_HANDLE  ImageHandle
  )
{
  UINTN  Index;
  UINTN  ConfigTableCount;
  EFI_CONFIGURATION_TABLE *ConfigTable;

  // Check if already cached
  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
        gHobList = ConfigTable[Index].VendorTable;
        break;
      }
    }
  }

  // If HOB list was not found, trigger assertion
  if (gHobList == NULL) {
    DebugPrint (DEBUG_INFO, "\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;
}

/**
 * Registers all IIO configuration protocol instances for this platform.
 *
 * This function implements the core purpose of this driver:
 *   1. Print a debug banner identifying this module as
 *      "UBA:IioCfgUpdate-TypeLightningRidgeEXECB1"
 *   2. Locate the UBA board-type protocol via gBS->LocateProtocol()
 *   3. Register up to 4 IIO configuration protocol instances
 *      (one per CPU socket) by calling the board-type protocol's
 *      registration function at interface offset 0x10
 *
 * Each registration call passes:
 *   - A unique IIO config protocol GUID (one per socket)
 *   - The same UBA_IIO_CFG_UPDATE_PROTOCOL structure (48 bytes)
 *     containing the IIO configuration table
 *
 * @return EFI_SUCCESS     All configurations were registered successfully.
 * @return Other           The board-type protocol could not be located, or
 *                         one or more registration calls failed.
 */
EFI_STATUS
RegisterIioConfig (
  VOID
  )
{
  EFI_STATUS              Status;
  UBA_BOARD_TYPE_PROTOCOL *Protocol;
  UINTN                   Index;

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

  // Locate the UBA board-type protocol
  Status = gBS->LocateProtocol (
    &gUbaBoardTypeProtocolGuid,  ///< Board-type protocol GUID
    NULL,                         ///< No registration handle
    (VOID **)&Protocol            ///< Returned protocol interface
    );

  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,                              ///< This protocol instance
      &gIioCfgUpdateProtocolGuid[Index],     ///< Socket-specific GUID
      (UBA_IIO_CFG_UPDATE_PROTOCOL *)&mIioCfgProtocol,  ///< IIO config data
      sizeof (mIioCfgProtocol)               ///< Size = 48 bytes
      );

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

  return Status;
}

/**
 * Module entry point for IioCfgUpdateDxeLightningRidgeEXECB1.
 *
 * Called by the DXE Dispatcher when this driver is loaded. Performs:
 *   1. Caches UEFI global variables (gImageHandle, gST, gBS, gRT)
 *      from the provided SystemTable
 *   2. Locates the HOB list from the system configuration table
 *      (required by GetDebugProtocol() which checks HOB size)
 *   3. Calls RegisterIioConfig() to register IIO configuration
 *      protocols for all CPU sockets
 *
 * @param[in] ImageHandle  The firmware-allocated handle for this driver image.
 * @param[in] SystemTable  A pointer to the EFI System Table.
 *
 * @return EFI_SUCCESS     IIO configuration was registered successfully.
 * @return Other           Registration failed.
 */
EFI_STATUS
EFIAPI
_ModuleEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS Status;

  // ==================================================================
  // Step 1: Cache UEFI global variables
  // ==================================================================
  // These are set by the UefiBootServicesTableLib and
  // UefiRuntimeServicesTableLib libraries at image startup.
  // The asserts below validate the pointers are non-NULL.
  // ==================================================================

  // 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
  // ==================================================================
  // The HOB list is needed by GetDebugProtocol() to check the HOB
  // allocation size before deciding whether to locate the debug protocol.
  // ==================================================================
  GetHobList (ImageHandle);

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

  return Status;
}