Newer
Older
AMI-Aptio-BIOS-Reversed / OpromUpdateDxeLightningRidgeEXECB3 / OpromUpdateDxeLightningRidgeEXECB3.c
@Ajax Dong Ajax Dong 2 days ago 37 KB Init
/**
 * @file OpromUpdateDxeLightningRidgeEXECB3.c
 *
 * @brief OpROM Update DXE Driver for LightningRidge EXEC B3 Platform.
 *
 * This is a UBA (Unified Board Architecture) board-type DXE driver that
 * registers PCIe Option ROM (OpROM) update configuration tables for the
 * LightningRidge EXEC B3 server platform (Intel Purley family).
 *
 * == Architecture Overview ==
 * The driver follows the standard UBA board-type pattern:
 * 1. Entry point caches UEFI globals from SystemTable
 * 2. HOB list is resolved via ConfigurationTable for platform info
 * 3. Platform debug banner ("UBA:OpromUpdate-TypeLightningRidgeEXECB3") is emitted
 * 4. UBA_CONFIG_PROTOCOL (GUID {E03E0D46...}) is located via gBS->LocateProtocol()
 * 5. A PBDS structure (48 bytes) with 5 callbacks is registered via RegisterConfig
 *
 * The 5 callbacks registered in the PBDS structure:
 *   [0] @+0x08: IsPcieSlotConfigured() - Slot range validation via PCI config space
 *   [1] @+0x10: OpromGetConfigA()     - Zero-initialized table (terminator)
 *   [2] @+0x18: OpromGetConfigB()     - PCIe config table (6 entries)
 *   [3] @+0x20: OpromGetConfigC()     - Extended PCIe config table (10 entries)
 *   [4] @+0x28: OpromSetSlotNumber()  - Slot number setter (always 0)
 *
 * == Key Differences from EXECB1/EXECB2 ==
 * - Platform string: "UBA:OpromUpdate-TypeLightningRidgeEXECB3"
 * - PDB: OpromUpdateDxeLightningRidgeEXECB3.pdb
 * - Otherwise structurally identical (same GUIDs, same callbacks, same data tables)
 *
 * == Binary Info ==
 * - Index: 31 of 427 PE files in HR650X BIOS
 * - SHA256: d58f84d1adb92017d59b450e242cac64939aadc6e24a35da7573064e7b531022
 * - PDB GUID: {2E401ABE-49E9-4BD3-969B-AF5A00349AA6}, Age 1
 * - Source path: e:\hs\PurleyRpPkg\Uba\UbaMain\Dxe\TypeLightningRidgeEXECB3\OpromUpdateDxe\
 * - Build: HR6N0XMLK\DEBUG_VS2015\X64
 */

#include "OpromUpdateDxeLightningRidgeEXECB3.h"

/*==============================================================================
 * GUID Definitions (stored in .rdata at fixed image offsets)
 *============================================================================*/

/**
 * EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_GUID
 * Standard UEFI protocol for PCI Root Bridge IO operations (config space access).
 * GUID: {2F707EBB-4A1A-11D4-9A38-0090273FC14D}
 * Image address: 0xCE0
 * Used by IsPcieSlotConfigured() via gBS->LocateProtocol().
 */
STATIC CONST EFI_GUID  gEfiPciRootBridgeIoProtocolGuid =
  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_GUID;

/**
 * UBA_DEBUG_LIB_PROTOCOL_GUID
 * Debug library protocol for UBA framework debug output and assertions.
 * GUID: {36232936-0E76-31C8-A13A-3AF2FC1C3932}
 * Image address: 0xCF0
 * Resolved lazily by GetDebugLibProtocol().
 */
STATIC CONST EFI_GUID  gUbaDebugLibProtocolGuid =
  UBA_DEBUG_LIB_PROTOCOL_GUID;

/**
 * UBA_CONFIG_PROTOCOL_GUID
 * Board-type configuration protocol for LightningRidge EXEC B3.
 * Provides RegisterConfig function at vtable offset 0x10.
 * GUID: {E03E0D46-5263-4845-B0A4-58D57B3177E2}
 * Image address: 0xD00
 */
STATIC CONST EFI_GUID  gUbaConfigProtocolGuid =
  UBA_CONFIG_PROTOCOL_GUID;

/**
 * EFI_HOB_LIST_GUID
 * Used to locate the HOB (Hand-Off Block) list via SystemTable->ConfigurationTable.
 * GUID: {7739F24C-93D7-11D4-9A3A-0090273FC14D}
 * Image address: 0xD10 (first 8 bytes), 0xD18 (second 8 bytes)
 */
STATIC CONST EFI_GUID  gEfiHobListGuid =
  EFI_HOB_LIST_GUID;

/**
 * OPROM_UPDATE_CONFIG_DATA_GUID
 * Configuration type GUID for the PBDS structure.
 * Passed to UBA_CONFIG_PROTOCOL.RegisterConfig().
 * GUID: {371BD79C-DE79-4C5F-AA2B-BC9EBEFA988F}
 * Image address: 0xEF0
 */
STATIC CONST EFI_GUID  gOpromUpdateConfigDataGuid =
  OPROM_UPDATE_CONFIG_DATA_GUID;

/*==============================================================================
 * Global Variable Definitions (.data segment, image addr 0xF30-0xF5F)
 *============================================================================*/

//
// UEFI standard protocol pointer caches (set by _ModuleEntryPoint)
// Image addr 0xF30: gST
// Image addr 0xF38: gBS
// Image addr 0xF40: gImageHandle
// Image addr 0xF48: gRT
//
EFI_SYSTEM_TABLE           *gST             = NULL;
EFI_BOOT_SERVICES          *gBS             = NULL;
EFI_HANDLE                  gImageHandle    = NULL;
EFI_RUNTIME_SERVICES       *gRT             = NULL;

//
// Cached protocol/hand-off block pointers (resolved lazily)
// Image addr 0xF50: gOpromProtocol  (UBA DebugLib protocol cache)
// Image addr 0xF58: gHobList        (HOB list pointer cache)
//
UBA_DEBUG_LIB_PROTOCOL     *gOpromProtocol  = NULL;
VOID                       *gHobList        = NULL;

/*==============================================================================
 * Configuration Data Structures
 *============================================================================*/

/**
 * PCIE_SLOT_RANGE table (image addr 0xED0, 8 entries of 4 bytes = 32 bytes)
 *
 * Encodes PCIe BDF slot ranges for 8 possible slots.
 * Read by IsPcieSlotConfigured() using byte-level stride of 4:
 *   Byte[-1] = Bus (or flags)
 *   Byte[0]  = Device number
 *   Byte[+1] = Function number (or continuation)
 *
 * The combined BDF address: ((Device | (Function << 8)) << 8 | Bus) << 8
 *   Entry 0: Bus=0x00, Dev=0x03, Func=0x00 -> 0x30000   (BDF 00:03.0)
 *   Entry 1: Bus=0x00, Dev=0x01, Func=0x80 -> 0x80010000 (BDF 00:01.0 with flag?)
 *   Entry 2: Bus=0x00, Dev=0x02, Func=0x80 -> 0x80020000 (BDF 00:02.0 with flag?)
 *   Entry 3: Bus=0x02, Dev=0x03, Func=0x80 -> 0x80030200 (BDF 02:03.2 with flag?)
 *   Entry 4: Bus=0x00, Dev=0x02, Func=0x00 -> 0x20000   (BDF 00:02.0)
 *   Entry 5: Bus=0x00, Dev=0x01, Func=0x00 -> 0x10000   (BDF 00:01.0)
 *   Entry 6: Bus=0x05, Dev=0x1c, Func=0x00 -> 0x1c0500  (BDF 05:1c.0)
 *   Entry 7: Bus=0x00, Dev=0x03, Func=0x80 -> 0x80030000 (BDF 00:03.0 with flag?)
 *
 * The Func=0x80 entries (entries 1,2,3,7) appear to encode a secondary
 * bus segment indicator (bit 7 set) rather than an actual function number.
 */
STATIC CONST PCIE_SLOT_RANGE  mPcieSlotRanges[8] = {
  { 0x00, 0x03, 0x00, 0x00 },  /* Entry 0: BDF 00:03.0 */
  { 0x00, 0x01, 0x80, 0x00 },  /* Entry 1: BDF 00:01.0 (seg 0x80) */
  { 0x00, 0x02, 0x80, 0x00 },  /* Entry 2: BDF 00:02.0 (seg 0x80) */
  { 0x02, 0x03, 0x80, 0x00 },  /* Entry 3: BDF 02:03.2 (seg 0x80) */
  { 0x00, 0x02, 0x00, 0x00 },  /* Entry 4: BDF 00:02.0 */
  { 0x00, 0x01, 0x00, 0x00 },  /* Entry 5: BDF 00:01.0 */
  { 0x05, 0x1C, 0x00, 0x00 },  /* Entry 6: BDF 05:1C.0 */
  { 0x00, 0x03, 0x80, 0x00 },  /* Entry 7: BDF 00:03.0 (seg 0x80) */
};

/**
 * Config Table A (image addr 0xD20, 64 bytes of zeros)
 *
 * Primary PCIe topology table. All zeros on this platform.
 * Acts as a terminator/sentinel - the UBA framework interprets this
 * as "no primary configuration". The function OpromGetConfigA() simply
 * returns a pointer to this zeroed area.
 *
 * The 0xFFFFFFFF at the end (offset 0xD50) might be a terminator value.
 */
STATIC CONST UINT64  mConfigTableA[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };

/**
 * Config Table B (image addr 0xD60, 6 entries of 8 bytes = 48 bytes)
 *
 * Device configuration entries for PCIe root ports and endpoints.
 * Each entry is 8 bytes, encoding:
 *   Bytes 0-3: Entry descriptor (type, flags, bus/device/function)
 *   Bytes 4-7: Extended descriptor (vendor/device ID, range info)
 *
 * Raw data (little-endian 64-bit values):
 *   [0] 0x0002000100000001  Type=1, flags=0x00020001, ...
 *   [1] 0x4801080215288086  Vendor=0x8086, Device=0x1528 (Intel I350)
 *   [2] 0x0000002d01000000  Revision=0, Class=0x01000000
 *   [3] 0x0000002c00000001  SubVendor=0x0001, SubSystem=0x0000002c
 *   [4] 0xff2fff2e00000001  Slot range: 0x2e-0x2f (or 0xff2f-0xff)
 *   [5] 0x0200010000000100  Extended config
 */
STATIC CONST UINT64  mConfigTableB[6] = {
  0x0002000100000001ULL,
  0x4801080215288086ULL,
  0x0000002d01000000ULL,
  0x0000002c00000001ULL,
  0xff2fff2e00000001ULL,
  0x0200010000000100ULL,
};

/**
 * Config Table C (image addr 0xE60, 10 entries of 8 bytes = 80 bytes)
 *
 * Extended device configuration entries.
 * These appear to describe Intel Ethernet controller configurations
 * including Intel I350 (0x1521, 0x1522) and 82599ES (0x10D3, 0x10FB).
 *
 * Raw data (little-endian 64-bit values):
 *   [0] 0x00010100358010d3  Dev=0x10D3:3580 (Intel 82599ES)
 *   [1] 0x0100358015210000  Dev=0x1521:3580 (Intel I350 variant)
 *   [2] 0x35b0152100000001  Dev=0x1521:35B0 (Intel I350 variant)
 *   [3] 0x1522000000010100  Dev=0x1522:3580 (Intel I350 variant)
 *   [4] 0x0000000101003580  Dev=0x...:3580 (Intel I350)
 *   [5] 0x0001010035561521  Dev=0x1521:3556 (Intel I350)
 *   [6] 0x0101355710fb0000  Dev=0x10FB:3557 (Intel 82599ES)
 *   [7] 0x3558152800000101  Dev=0x1528:3558 (Intel I350)
 *   [8] 0x1528000001010101  Dev=0x1528 (Intel I350)
 *   [9] 0x00000101010135c5  Extended entry
 */
STATIC CONST UINT64  mConfigTableC[10] = {
  0x00010100358010d3ULL,
  0x0100358015210000ULL,
  0x35b0152100000001ULL,
  0x1522000000010100ULL,
  0x0000000101003580ULL,
  0x0001010035561521ULL,
  0x0101355710fb0000ULL,
  0x3558152800000101ULL,
  0x1528000001010101ULL,
  0x00000101010135c5ULL,
};

/**
 * OPROM_UPDATE_CONFIG (PBDS structure at image addr 0xF00, 48 bytes)
 *
 * Registered with the UBA_CONFIG_PROTOCOL.RegisterConfig() call in
 * _ModuleEntryPoint. Contains 5 callback function pointers that the
 * UBA framework uses to query platform-specific OpROM update info.
 *
 * The .reloc section has entries for the address range 0xF08-0xF28
 * (the function pointer fields), ensuring proper relocation at runtime.
 */
STATIC CONST OPROM_UPDATE_CONFIG  mOpromUpdateConfig = {
  .Signature            = 0x53444250,  /* 'PBDS' */
  .Version              = 1,
  .IsPcieSlotConfigured = (UINT64)(UINTN)IsPcieSlotConfigured,
  .OpromGetConfigA      = (UINT64)(UINTN)OpromGetConfigA,
  .OpromGetConfigB      = (UINT64)(UINTN)OpromGetConfigB,
  .OpromGetConfigC      = (UINT64)(UINTN)OpromGetConfigC,
  .OpromSetSlotNumber   = (UINT64)(UINTN)OpromSetSlotNumber,
};

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

/**
 * @brief UEFI DXE driver entry point.
 *
 * Called by the DXE Dispatcher after all dependencies are resolved.
 *
 * Initialization sequence:
 * 1. Cache ImageHandle, SystemTable, BootServices, RuntimeServices globals
 * 2. Resolve the HOB list via GetHobList() (uses SystemTable->ConfigurationTable)
 * 3. Emit the platform identification banner via DebugPrint()
 * 4. Locate UBA_CONFIG_PROTOCOL by GUID {E03E0D46-5263-4845-B0A4-58D57B3177E2}
 * 5. Call the protocol's RegisterConfig (vtable[2] @ offset 0x10) with:
 *    - Config type GUID: {371BD79C-DE79-4C5F-AA2B-BC9EBEFA988F}
 *    - Config data: &mOpromUpdateConfig (PBDS, 48 bytes)
 *    - Data size: 48 (sizeof(OPROM_UPDATE_CONFIG))
 *
 * @param[in] ImageHandle  UEFI image handle for this driver.
 * @param[in] SystemTable  UEFI system table pointer.
 *
 * @return EFI_SUCCESS           Configuration registered successfully.
 * @return EFI_UNSUPPORTED/...   LocateProtocol or RegisterConfig failure.
 *
 * @note The UefiBootServicesTableLib and UefiRuntimeServicesTableLib
 *       library constructors set the module-level gImageHandle, gST,
 *       gBS, and gRT globals. The entry point's explicit caching here
 *       duplicates that functionality for safety.
 */
EFI_STATUS
EFIAPI
_ModuleEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;
  VOID        *UbaConfigProtocol;     /* Protocol interface from LocateProtocol */
  UINT64      ConfigBuffer;           /* Output buffer for LocateProtocol */

  //
  // Step 1: Cache UEFI standard protocol pointers.
  // These are also set by the library constructors in
  // UefiBootServicesTableLib and UefiRuntimeServicesTableLib,
  // but we cache them explicitly here for robustness.
  //
  gImageHandle = ImageHandle;
  gST          = SystemTable;
  gBS          = SystemTable->BootServices;
  gRT          = SystemTable->RuntimeServices;

  //
  // Step 2: Resolve the HOB (Hand-Off Block) list.
  // This walks SystemTable->ConfigurationTable[] looking for the entry
  // with VendorGuid == EFI_HOB_LIST_GUID ({7739F24C-...}).
  // The associated VendorTable pointer is the HOB list header.
  // Result is cached in gHobList (image addr 0xF58).
  //
  GetHobList ();

  //
  // Step 3: Print the platform identification banner.
  // ErrorLevel 0x80000000 is the UBA-specific debug severity mask.
  //
  DebugPrint (0x80000000, "UBA:OpromUpdate-TypeLightningRidgeEXECB3\n");

  //
  // Step 4: Locate the UBA_CONFIG_PROTOCOL.
  // GUID {E03E0D46-5263-4845-B0A4-58D57B3177E2} identifies the
  // platform-specific UBA configuration protocol.
  //
  ConfigBuffer = 0;
  Status = gBS->LocateProtocol (
                  &gUbaConfigProtocolGuid,
                  NULL,                                 /* No registration */
                  (VOID **)&ConfigBuffer                /* Output interface */
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Step 5: Call the protocol's RegisterConfig function.
  // The function pointer is at vtable index 2 (offset 0x10).
  // Signature:
  //   EFI_STATUS (*RegisterConfig)(
  //     VOID *This,
  //     CONST EFI_GUID *ConfigGuid,
  //     CONST VOID *ConfigData,
  //     UINTN ConfigDataSize
  //     );
  //
  // Parameters:
  //   ConfigGuid = &gOpromUpdateConfigDataGuid
  //                {371BD79C-DE79-4C5F-AA2B-BC9EBEFA988F}
  //   ConfigData = &mOpromUpdateConfig (PBDS structure, 48 bytes)
  //   DataSize   = 48 (sizeof(OPROM_UPDATE_CONFIG))
  //
  UbaConfigProtocol = (VOID *)ConfigBuffer;
  return ((EFI_STATUS (*)(VOID *, CONST EFI_GUID *, CONST VOID *, UINTN))
          (*((UINT64 **)UbaConfigProtocol) + 2)) (  /* vtable[2] = offset 0x10 */
           UbaConfigProtocol,
           &gOpromUpdateConfigDataGuid,
           &mOpromUpdateConfig,
           sizeof (OPROM_UPDATE_CONFIG)
           );
}

/**
 * @brief Lazily resolves and caches the UBA DebugLib protocol.
 *
 * Called on first use from DebugPrint() or DebugAssert(). The protocol
 * is resolved only once and cached in gOpromProtocol (image addr 0xF50).
 *
 * Resolution steps:
 * 1. Check cache: if gOpromProtocol is non-NULL, return immediately
 * 2. Pool check: allocate and free a 31-byte pool. If only <= 0x10 bytes
 *    available, return NULL (minimum pool size sanity check)
 * 3. LocateProtocol: call gBS->LocateProtocol() with
 *    UBA_DEBUG_LIB_PROTOCOL_GUID {36232936-0E76-31C8-A13A-3AF2FC1C3932}
 * 4. On success, cache the result and return; on failure, cache NULL
 *
 * @return Pointer to UBA_DEBUG_LIB_PROTOCOL, or NULL if:
 *         - Pool allocation check fails (<= 0x10 bytes)
 *         - gBS->LocateProtocol() fails
 *
 * @note The pool allocation/free sequence (UINTN type 31, then free) is
 *       a UEFI memory subsystem sanity check inherited from the library.
 */
UBA_DEBUG_LIB_PROTOCOL *
GetDebugLibProtocol (
  VOID
  )
{
  UINTN   PoolSize;            /* Dummy pool allocation for size check */
  UINT64  ProtocolPtr;         /* Output buffer for LocateProtocol */

  //
  // Return cached pointer if already resolved.
  //
  if (gOpromProtocol != NULL) {
    return gOpromProtocol;
  }

  //
  // Check available pool size by allocating and immediately freeing.
  // Type 31 is EfiBootServicesData in this platform's memory map.
  // If only <= 0x10 bytes available, return NULL.
  //
  gBS->AllocatePool (EfiBootServicesData, 31, (VOID **)&PoolSize);
  gBS->FreePool ((VOID *)PoolSize);

  if (PoolSize <= 0x10) {
    gOpromProtocol = NULL;
    return NULL;
  }

  //
  // Resolve the UBA DebugLib protocol.
  //
  ProtocolPtr = 0;
  if (EFI_ERROR (gBS->LocateProtocol (
                        &gUbaDebugLibProtocolGuid,
                        NULL,
                        (VOID **)&ProtocolPtr
                        ))) {
    gOpromProtocol = NULL;
    return NULL;
  }

  gOpromProtocol = (UBA_DEBUG_LIB_PROTOCOL *)ProtocolPtr;
  return gOpromProtocol;
}

/**
 * @brief Platform-aware debug output with CMOS-based board type detection.
 *
 * Reads CMOS register 0x4B (via legacy I/O ports 0x70/0x71) to determine
 * the board platform type, which selects the debug output mask:
 *
 *   Platform type 1 (LightningRidge):
 *     Debug mask = 0x80000004
 *   Platform type 2 or 3 (other platforms):
 *     Debug mask = 0x80000006
 *
 * If CMOS register 0x4B reads as 0 (uninitialized), falls back to MMIO
 * read at physical address 0xFDAF0490 bit 1, ORed with 1, to determine
 * the platform type.
 *
 * Debug output is only emitted when (ErrorLevel & DebugMask) != 0.
 * The function returns 4 (not 0) when output is suppressed but the
 * protocol and platform check succeeded - this distinguishes "suppressed"
 * from "protocol missing".
 *
 * @param[in] ErrorLevel  Debug message severity level mask.
 * @param[in] Format      Printf-compatible format string.
 * @param[in] ...         Variable arguments for format string.
 *
 * @return Non-zero  Debug output was emitted or successfully suppressed.
 * @return 0         DebugLib protocol not available (GetDebugLibProtocol failed).
 */
UINTN
EFIAPI
DebugPrint (
  IN UINTN       ErrorLevel,
  IN CONST CHAR8 *Format,
  ...
  )
{
  VA_LIST                   VaList;
  UBA_DEBUG_LIB_PROTOCOL    *Protocol;
  UINTN                     PlatformType;    /* CMOS platform type value */
  UINTN                     DebugMask;       /* Platform-specific debug mask */

  //
  // Resolve the DebugLib protocol (lazy initialization).
  //
  Protocol = GetDebugLibProtocol ();
  if (Protocol == NULL) {
    return 0;
  }

  //
  // Read CMOS register 0x4B to determine the board platform type.
  // CMOS access sequence:
  //   1. Read index register (0x70) to preserve NMI disable bit
  //   2. Write index with NMI-disabled + 0x4B
  //   3. Read data register (0x71)
  //
  PlatformType = IoRead8 (0x70);                              /* Save NMI state */
  IoWrite8 (0x70, (PlatformType & 0x80) | 0x4B);             /* Select CMOS 0x4B */
  PlatformType = IoRead8 (0x71);                              /* Read platform type */

  //
  // If CMOS returned > 3 and == 0, the register is uninitialized.
  // Fall back to MMIO at 0xFDAF0490 (GPIO or strapping register).
  // Extract bit 1, OR with 1 to get platform type 1 or 3.
  //
  if (PlatformType > 3) {
    if (PlatformType == 0) {
      PlatformType = (*(volatile UINT8 *)0xFDAF0490 & 2) | 1;
    }
  }

  //
  // Select debug mask based on platform type.
  //
  if (PlatformType == 1) {
    DebugMask = 0x80000004;           /* LightningRidge specific mask */
  } else {
    DebugMask = 0x80000006;           /* Other platform mask */
  }

  //
  // Check if the error level matches the platform debug mask.
  // If no bits match, suppress the output.
  //
  if ((DebugMask & ErrorLevel) == 0) {
    return 4;                         /* Successfully suppressed (not error) */
  }

  //
  // Call the protocol's DebugPrint function at vtable offset 0x00.
  // The protocol's DebugPrint uses VA_LIST, not variadic args.
  //
  VA_START (VaList, Format);
  return ((UINTN (*)(UINTN, CONST CHAR8 *, VA_LIST))
          Protocol->DebugPrint) (ErrorLevel, Format, VaList);
}

/**
 * @brief ASSERT failure handler via DebugLib protocol.
 *
 * Called when an ASSERT() condition evaluates to FALSE in the module.
 * Routes to the UBA DebugLib protocol's assertion handler at protocol
 * vtable offset 0x08.
 *
 * The assertion information includes the source file path, line number,
 * and a text description of the assertion condition.
 *
 * @param[in] FileName     Full path to the source file containing the assert.
 * @param[in] LineNumber   Line number of the assertion in the source file.
 * @param[in] Description  Text description of the failing assertion condition.
 */
VOID
DebugAssert (
  IN CONST CHAR8  *FileName,
  IN UINTN        LineNumber,
  IN CONST CHAR8  *Description
  )
{
  UBA_DEBUG_LIB_PROTOCOL  *Protocol;

  //
  // Resolve the DebugLib protocol (lazy initialization).
  //
  Protocol = GetDebugLibProtocol ();
  if (Protocol != NULL) {
    //
    // Call the protocol's assertion handler at vtable offset 0x08.
    //
    ((VOID (*)(CONST CHAR8 *, UINTN, CONST CHAR8 *))
     Protocol->DebugAssert) (FileName, LineNumber, Description);
  }
}

/**
 * @brief Locate the HOB (Hand-Off Block) list from UEFI configuration table.
 *
 * The HOB list pointer is stored in the UEFI system table's configuration
 * table array, keyed by EFI_HOB_LIST_GUID ({7739F24C-93D7-11D4-9A3A-
 * 0090273FC14D}). This function scans gST->ConfigurationTable[] to find it.
 *
 * The system table layout for HOB access:
 *   gST->NumberOfTableEntries  = *(UINTN *)(gST + 0x68)
 *   gST->ConfigurationTable    = *(EFI_CONFIGURATION_TABLE **)(gST + 0x70)
 *   Each entry is 24 bytes: EFI_GUID (16) + VOID* (8)
 *
 * Once found, the result is cached in gHobList (image addr 0xF58) for
 * O(1) subsequent access.
 *
 * Error handling:
 * - If the HOB list GUID is NOT found in any ConfigurationTable entry,
 *   triggers DebugAssert with status 0x800000000000000E (EFI_NOT_FOUND)
 * - If the resolved pointer is NULL, another DebugAssert is triggered
 * - Both ASSERTs reference e:\hs\MdePkg\Library\DxeHobLib\HobLib.c
 *   (lines 54 and 55)
 *
 * @return Pointer to the HOB list, or NULL if not found / resolution fails.
 */
VOID *
GetHobList (
  VOID
  )
{
  UINTN                     Index;
  UINTN                     TableCount;
  EFI_CONFIGURATION_TABLE   *ConfigTable;

  //
  // Return cached pointer if already resolved.
  //
  if (gHobList != NULL) {
    return gHobList;
  }

  gHobList = NULL;

  //
  // Access the configuration table array from the system table.
  // SystemTable + 0x68 = NumberOfTableEntries (UINTN)
  // SystemTable + 0x70 = ConfigurationTable (EFI_CONFIGURATION_TABLE *)
  //
  TableCount  = gST->NumberOfTableEntries;
  ConfigTable = gST->ConfigurationTable;

  //
  // Iterate through the configuration table entries to find the HOB list.
  // Each entry is 24 bytes: 16-byte VendorGuid + 8-byte VendorTable pointer.
  // We compare the first 8 bytes and next 8 bytes separately via
  // ReadUnaligned64 against the reference GUID halves.
  //
  if (ConfigTable != NULL) {
    for (Index = 0; Index < TableCount; Index++) {
      if (IsHobListGuid (&ConfigTable[Index].VendorGuid)) {
        //
        // Found the HOB list GUID. The associated VendorTable pointer
        // is the HOB list (PHIT HOB header).
        //
        gHobList = (VOID *)ConfigTable[Index].VendorTable;
        break;
      }
    }
  }

  //
  // If we didn't find the GUID in any table entry, trigger an ASSERT.
  //
  if (gHobList == NULL) {
    DebugPrint (
      0x80000000,
      "\nASSERT_EFI_ERROR (Status = %r)\n",
      0x800000000000000EULL      /* EFI_NOT_FOUND status */
      );
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
      54,
      "!EFI_ERROR (Status)"
      );
  }

  //
  // If the resolved pointer is NULL, trigger another ASSERT.
  //
  if (gHobList == NULL) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
      55,
      "mHobList != ((void *) 0)"
      );
  }

  return gHobList;
}

/**
 * @brief Check if a GUID matches EFI_HOB_LIST_GUID using 64-bit halves.
 *
 * Compares the input GUID against the reference EFI_HOB_LIST_GUID
 * ({7739F24C-93D7-11D4-9A3A-0090273FC14D}) by splitting both into
 * two 64-bit halves and comparing each half via ReadUnaligned64().
 *
 * The reference GUID halves are stored at:
 *   0xD10: First 8 bytes = {7739F24C-93D7-11D4}
 *   0xD18: Second 8 bytes = {9A3A0090-273FC14D}
 *
 * This avoids potential alignment issues that could arise from
 * direct EFI_GUID struct comparison, since EFI_GUID has specific
 * alignment requirements (4-byte aligned Data1).
 *
 * @param[in] Guid  Pointer to the GUID to compare.
 *
 * @return TRUE   The GUID matches EFI_HOB_LIST_GUID.
 * @return FALSE  The GUID does not match.
 */
BOOLEAN
IsHobListGuid (
  IN EFI_GUID  *Guid
  )
{
  //
  // Compare the GUID as two 64-bit halves.
  // First half: Data1(4) + Data2(2) + Data3(2) = 8 bytes (little-endian)
  // Second half: Data4(8) = 8 bytes
  //
  return (ReadUnaligned64 (&gEfiHobListGuid) == ReadUnaligned64 (Guid))
      && (ReadUnaligned64 ((UINT8 *)&gEfiHobListGuid + 8)
            == ReadUnaligned64 ((UINT8 *)Guid + 8));
}

/**
 * @brief Read a 64-bit value from an (possibly unaligned) pointer.
 *
 * Directly dereferences a 64-bit pointer without alignment checks.
 * Asserts via DebugAssert() if the pointer is NULL.
 *
 * This function is the module-level implementation of the BaseLib
 * ReadUnaligned64() function, with the same behavior.
 *
 * Source path (from ASSERT string):
 *   e:\hs\MdePkg\Library\BaseLib\Unaligned.c, line 192
 *
 * @param[in] Buffer  Pointer to read from (must not be NULL).
 *
 * @return The 64-bit value at the Buffer address.
 */
UINT64
ReadUnaligned64 (
  IN CONST VOID  *Buffer
  )
{
  //
  // Assert if the pointer is NULL.
  //
  if (Buffer == NULL) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\BaseLib\\Unaligned.c",
      192,
      "Buffer != ((void *) 0)"
      );
  }

  //
  // Dereference the pointer directly.
  //
  return *(CONST UINT64 *)Buffer;
}

/**
 * @brief Check if a PCIe BDF address is within a configured slot's range.
 *
 * UBA framework callback, invoked by the platform to determine if a
 * PCIe device at the given BDF address should receive an OpROM update.
 *
 * Algorithm:
 * 1. Iterate the 8 mPcieSlotRanges entries at image addr 0xED0
 * 2. For each slot entry where the corresponding SlotMask bit is CLEAR
 *    (bit=0 means the slot is NOT already configured and needs checking):
 *    a. Compute the BDF address from the 3-byte descriptor:
 *       combined = ((Device | (Function << 8)) << 8 | Bus) << 8
 *       This creates a PciLib address offset (register field = 0)
 *    b. Open EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL via gBS->LocateProtocol()
 *    c. Read PCI config register offset 0x19 (Secondary Bus Number / BIST)
 *    d. Read PCI config register offset 0x1A (Subordinate Bus / Hdr Type)
 *    e. Check: BIST_val <= PcieBdfAddr <= HdrType_val
 *       (The BIST and Header Type registers are repurposed on Intel
 *        chipsets to encode secondary bus range information for PCIe
 *        root ports)
 * 3. Return TRUE if any slot range contains the target BDF address
 *
 * Note: gBS->LocateProtocol() is called INSIDE the loop for each slot,
 * not cached. This is how the original binary behaves.
 *
 * @param[in] PcieBdfAddr  PCIe BDF address to test (bus:device:function).
 *                          Encoded as a 64-bit PciLib address with the
 *                          register offset bits set to 0.
 * @param[in] SlotMask     Bitmask of slots (bits 0-7). A clear bit means
 *                          the slot should be checked. A set bit means
 *                          the slot is already configured.
 *
 * @return TRUE   The PCIe BDF address falls within a configured slot range.
 * @return FALSE  The BDF is outside all slot ranges, or the PCI Root Bridge
 *                IO protocol could not be located.
 */
BOOLEAN
IsPcieSlotConfigured (
  IN UINT64   PcieBdfAddr,
  IN UINT32   SlotMask
  )
{
  CONST UINT8       *SlotData;     /* Pointer walking through slot config table */
  BOOLEAN           Found;         /* Result accumulator */
  UINT16            SlotIndex;     /* Loop counter for 8 slot entries */
  UINT32            CurrentMask;   /* Local copy of SlotMask (updated by loop) */

  //
  // Initialize the table pointer to start BEFORE the first entry.
  // The original code uses &unk_ED1 as the base, then reads 3 bytes
  // at offsets [-1, 0, +1], advancing by 4 each iteration.
  // Our implementation uses direct indexing into mPcieSlotRanges[],
  // which avoids the +1 offset trick.
  //
  SlotData = (CONST UINT8 *)&mPcieSlotRanges;
  Found    = FALSE;
  CurrentMask = SlotMask;

  //
  // Iterate each of the 8 PCIe slot range entries.
  //
  for (SlotIndex = 0; SlotIndex < 8; SlotIndex++) {
    //
    // Check if this slot bit is clear in the mask.
    // A clear bit (0) means the slot is NOT yet configured and
    // should be checked for device presence.
    //
    if (((CurrentMask >> SlotIndex) & 1) == 0) {
      UINT8   BusByte;          /* Byte at slot entry offset 0 (Bus) */
      UINT8   DevByte;          /* Byte at slot entry offset 1 (Device) */
      UINT8   FuncByte;         /* Byte at slot entry offset 2 (Function) */
      UINT64  SlotBdfAddr;      /* Computed BDF address for this slot */
      UINT64  PciRootBridgeIo;  /* Protocol interface from LocateProtocol */
      UINT8   RegValue19;       /* Value of PCI config register 0x19 */
      UINT8   RegValue1A;       /* Value of PCI config register 0x1A */

      //
      // Extract the 3 bytes from the slot entry.
      // In the original assembly:
      //   v8  = byte[-1] (mPcieSlotRanges[SlotIndex].Bus)
      //   v9  = byte[0]  (mPcieSlotRanges[SlotIndex].Device)
      //   v10 = byte[+1] (mPcieSlotRanges[SlotIndex].Function)
      //
      BusByte  = mPcieSlotRanges[SlotIndex].Bus;
      DevByte  = mPcieSlotRanges[SlotIndex].Device;
      FuncByte = mPcieSlotRanges[SlotIndex].Function;

      //
      // Compute the BDF address from the 3-byte descriptor.
      // Formula: ((Device | (Function << 8)) << 8 | Bus) << 8
      // This gives a PciLib-style address with register field = 0.
      //
      SlotBdfAddr = (((DevByte | ((UINT64)FuncByte << 8)) << 8)
                     | (UINT64)BusByte) << 8;

      //
      // Open EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL to access PCI config space.
      // Note: This is done INSIDE the loop per the original binary behavior.
      // The protocol GUID is {2F707EBB-4A1A-11D4-9A38-0090273FC14D}.
      //
      gBS->LocateProtocol (
             &gEfiPciRootBridgeIoProtocolGuid,
             NULL,
             (VOID **)&PciRootBridgeIo
             );

      if (PciRootBridgeIo != 0) {
        //
        // Read PCI config space register at offset 0x19.
        // On PCIe root ports, this is typically the Secondary Bus Number
        // register. On standard PCI devices, this is BIST (Built-In Self Test).
        // Intel chipsets repurpose this register to encode the start of
        // the secondary bus range for this slot.
        //
        // The Pci.Read function at vtable index 7 (offset 0x38) is used.
        // Signature:
        //   Pci.Read(This, Width=0 (Uint8), Address, Count, Buffer)
        //
        ((VOID (*)(UINT64, UINT64, UINT64, UINT64, UINT8 *))
         (*((UINT64 **)PciRootBridgeIo) + 7)) (  /* vtable[7] = offset 0x38 */
          PciRootBridgeIo,
          0,                   /* EfiPciWidthUint8 */
          SlotBdfAddr | 0x19,  /* Address: BDF | register 0x19 */
          1,                   /* Count: 1 byte */
          &RegValue19
          );

        //
        // Read PCI config space register at offset 0x1A.
        // This is the Subordinate Bus Number register on PCIe root ports,
        // or Header Type on standard devices. Encodes the end of the
        // secondary bus range for this slot.
        //
        ((VOID (*)(UINT64, UINT64, UINT64, UINT64, UINT8 *))
         (*((UINT64 **)PciRootBridgeIo) + 7)) (
          PciRootBridgeIo,
          0,
          SlotBdfAddr | 0x1A,  /* Address: BDF | register 0x1A */
          1,
          &RegValue1A
          );

        //
        // Check if the target BDF address falls within the slot's range.
        // If reg_0x19 <= PcieBdfAddr <= reg_0x1A, the device at this
        // BDF is in the slot's secondary bus range.
        //
        if ((PcieBdfAddr >= RegValue19) && (PcieBdfAddr <= RegValue1A)) {
          Found = TRUE;
        }
      }
    }
  }

  return Found;
}

/**
 * @brief Get pointer to Config Table A (primary PCIe topology).
 *
 * UBA framework callback registered in PBDS at offset +0x10.
 * Returns a pointer to mConfigTableA (image addr 0xD20), which is
 * a zero-initialized 64-byte buffer. On the LightningRidge EXEC B3
 * platform, this table is unused - it acts as a terminator/null
 * sentinel for the UBA framework.
 *
 * @param[out] ConfigTable  Receives pointer to mConfigTableA.
 *
 * @return EFI_SUCCESS  Always.
 */
EFI_STATUS
OpromGetConfigA (
  OUT VOID  **ConfigTable
  )
{
  *ConfigTable = (VOID *)&mConfigTableA;
  return EFI_SUCCESS;
}

/**
 * @brief Get pointer to Config Table B (6 PCIe root port entries).
 *
 * UBA framework callback registered in PBDS at offset +0x18.
 * Returns pointer to mConfigTableB (image addr 0xD60) with 6 entries
 * of 8 bytes each.
 *
 * The table encodes PCIe root port configuration for OpROM updates,
 * including vendor/device IDs (Intel I350 - 0x8086:0x1528) and slot
 * range information.
 *
 * @param[out] ConfigTable  Receives pointer to mConfigTableB.
 * @param[out] EntryCount   Receives entry count (always 6).
 *
 * @return EFI_SUCCESS  Always.
 */
EFI_STATUS
OpromGetConfigB (
  OUT VOID    **ConfigTable,
  OUT UINTN   *EntryCount
  )
{
  *ConfigTable = (VOID *)&mConfigTableB;
  *EntryCount  = 6;
  return EFI_SUCCESS;
}

/**
 * @brief Get pointer to Config Table C (10 extended PCIe entries).
 *
 * UBA framework callback registered in PBDS at offset +0x20.
 * Returns pointer to mConfigTableC (image addr 0xE60) with 10 entries
 * of 8 bytes each.
 *
 * The table contains extended device configuration for various Intel
 * Ethernet controllers:
 *   - 0x8086:0x10D3  Intel 82599ES 10 Gigabit Ethernet
 *   - 0x8086:0x1521  Intel I350 Gigabit Ethernet
 *   - 0x8086:0x1522  Intel I350 Gigabit Ethernet (alternate)
 *   - 0x8086:0x1528  Intel I350 Gigabit Ethernet
 *   - 0x8086:0x10FB  Intel 82599ES 10 Gigabit Ethernet (alternate)
 *
 * @param[out] ConfigTable  Receives pointer to mConfigTableC.
 * @param[out] EntryCount   Receives entry count (always 10).
 *
 * @return EFI_SUCCESS  Always.
 */
EFI_STATUS
OpromGetConfigC (
  OUT VOID    **ConfigTable,
  OUT UINTN   *EntryCount
  )
{
  *ConfigTable = (VOID *)&mConfigTableC;
  *EntryCount  = 10;
  return EFI_SUCCESS;
}

/**
 * @brief Set PCIe slot number callback.
 *
 * UBA framework callback registered in PBDS at offset +0x28.
 * On the LightningRidge EXEC B3 platform, this function always sets
 * the slot number to 0 and logs a debug message.
 *
 * The debug message: "[UBA]:SetPcieSlotNumber callback - %d\n"
 * with argument 0, indicating that slot enumeration should start
 * from slot number 0 for this platform.
 *
 * @param[out] SlotNumber  Receives the slot number (always 0).
 *
 * @return EFI_SUCCESS  Always.
 */
EFI_STATUS
OpromSetSlotNumber (
  OUT UINT8  *SlotNumber
  )
{
  //
  // Always set slot number to 0 on this platform.
  //
  *SlotNumber = 0;

  //
  // Log the slot assignment: "[UBA]:SetPcieSlotNumber callback - 0\n"
  //
  DebugPrint (0x80000000, "[UBA]:SetPcieSlotNumber callback - %d\n", 0);

  return EFI_SUCCESS;
}

/*==============================================================================
 * Module Summary
 *
 * == Binary Identity ==
 * File:    OpromUpdateDxeLightningRidgeEXECB3.efi
 * Index:   31 of 427 PE files in HR650X BIOS
 * Size:    4224 bytes (6 PE sections)
 * SHA256:  d58f84d1adb92017d59b450e242cac64939aadc6e24a35da7573064e7b531022
 * PDB:     OpromUpdateDxeLightningRidgeEXECB3.pdb
 * PDB GUID: {2E401ABE-49E9-4BD3-969B-AF5A00349AA6}, Age 1
 *
 * == Source Path (from PDB) ==
 * e:\hs\Build\HR6N0XMLK\DEBUG_VS2015\X64\PurleyRpPkg\Uba\UbaMain\Dxe\
 *     TypeLightningRidgeEXECB3\OpromUpdateDxe\OpromUpdateDxe\DEBUG\
 *     OpromUpdateDxeLightningRidgeEXECB3.pdb
 *
 * == Protocol GUIDs Used ==
 *   0xCE0: {2F707EBB-4A1A-11D4-9A38-0090273FC14D}  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL
 *   0xCF0: {36232936-0E76-31C8-A13A-3AF2FC1C3932}  UBA_DEBUG_LIB_PROTOCOL
 *   0xD00: {E03E0D46-5263-4845-B0A4-58D57B3177E2}  UBA_CONFIG_PROTOCOL (EXECB3)
 *   0xD10: {7739F24C-93D7-11D4-9A3A-0090273FC14D}  EFI_HOB_LIST_GUID
 *   0xEF0: {371BD79C-DE79-4C5F-AA2B-BC9EBEFA988F}  OPROM_UPDATE_CONFIG_DATA
 *
 * == PCI Devices Identified in Config Tables ==
 *   0x8086:0x1528  Intel I350 Gigabit Ethernet
 *   0x8086:0x1521  Intel I350 GB Ethernet (alternate variant)
 *   0x8086:0x1522  Intel I350 GB Ethernet (another variant)
 *   0x8086:0x10D3  Intel 82599ES 10 Gigabit Ethernet
 *   0x8086:0x10FB  Intel 82599ES 10 Gigabit (alternate variant)
 *
 * == Compiled Library Paths (from ASSERT strings) ==
 *   e:\hs\MdePkg\Library\UefiBootServicesTableLib\UefiBootServicesTableLib.c
 *   e:\hs\MdePkg\Library\UefiRuntimeServicesTableLib\UefiRuntimeServicesTableLib.c
 *   e:\hs\MdePkg\Library\DxeHobLib\HobLib.c
 *   e:\hs\MdePkg\Library\BaseLib\Unaligned.c
 *
 * == Section Layout ==
 *   HEADER  0x000-0x2C0  PE headers and DOS stub
 *   .text   0x2C0-0x8DF  Executable code (12 functions)
 *   .rdata  0x8E0-0xB3F  Read-only data (strings, debug info)
 *   .data   0xB40-0xB73  Uninitialized globals (zeros)
 *           0xB74-0xBFF  PDB/RSDS debug record
 *   seg004  0xBE0-0xC3F  Second PDB path string
 *   .xdata  0x1000-0x105F Exception handling data
 *   .reloc  0x1060-0x1073 Base relocations (6 entries for PBDS func ptrs)
 *
 * == Function Index ==
 *   0x0390: _ModuleEntryPoint         Entry Point (EFIAPI)
 *   0x048C: IsPcieSlotConfigured      PBDS callback [0] @+0x08
 *   0x05B8: OpromGetConfigA           PBDS callback [1] @+0x10
 *   0x05C8: OpromGetConfigB           PBDS callback [2] @+0x18
 *   0x05DC: OpromGetConfigC           PBDS callback [3] @+0x20
 *   0x05F0: OpromSetSlotNumber        PBDS callback [4] @+0x28
 *   0x0614: GetDebugLibProtocol       Helper (lazy protocol resolver)
 *   0x0694: DebugPrint                Helper (CMOS-based debug output)
 *   0x071C: DebugAssert               Helper (assert handler)
 *   0x075C: GetHobList                Helper (HOB list resolver)
 *   0x0834: IsHobListGuid             Helper (GUID comparator)
 *   0x08A4: ReadUnaligned64           Helper (unaligned read)
 *============================================================================*/