/**
 * @file OpromUpdateDxeLightningRidgeEXECB1.c
 *
 * @brief OpromUpdateDxeLightningRidgeEXECB1 - UEFI DXE driver implementation.
 *        UBA board-type driver for Lightning Ridge EXEC B1 PCIe OpROM update
 *        configuration.
 *
 * This driver registers platform-specific PCIe Option ROM update policy with
 * the UBA (Unified Board Architecture) framework. It provides callback-based
 * configuration tables describing which PCIe slots and devices should receive
 * OpROM updates.
 *
 * ADDRESS RANGE: 0x2C0 - 0x8E0 (code segment, 0x620 bytes)
 * ENTRY POINT:   _ModuleEntryPoint at 0x390
 * TOTAL FUNCTIONS: 12
 *
 * FUNCTION INDEX
 *   _ModuleEntryPoint  (0x390) - Driver entry point
 *   IsPcieSlotConfigured (0x48C) - Check if BDF is in a configured slot
 *   OpromGetConfigA    (0x5B8) - Callback: return table A pointer
 *   OpromGetConfigB    (0x5C8) - Callback: return table B pointer+count
 *   OpromGetConfigC    (0x5DC) - Callback: return table C pointer+count
 *   OpromSetSlotNumber (0x5F0) - Callback: set slot number to 0
 *   GetDebugLibProtocol (0x614) - Resolve DebugLib/OpROM protocol interface
 *   DebugPrint          (0x694) - Debug output via DebugLib protocol
 *   DebugAssert         (0x71C) - Assertion failure handler
 *   GetHobList          (0x75C) - Locate HOB list from system table
 *   IsHobListGuid       (0x834) - Compare GUID against HOB list GUID
 *   ReadUnaligned64     (0x8A4) - Unaligned 64-bit memory read
 */

#include "OpromUpdateDxeLightningRidgeEXECB1.h"

#include <stdarg.h>    // va_list, va_start, va_end
#include <stddef.h>    // NULL

// ============================================================================
// Platform I/O Intrinsics (MSVC __inbyte / __outbyte replacements)
// ============================================================================

#if defined(__GNUC__) || defined(__clang__)

static __inline__
UINT8
__inbyte (
  UINT16  Port
  )
{
  UINT8  Value;
  __asm__ __volatile__ ("inb %1, %0" : "=a" (Value) : "d" (Port));
  return Value;
}

static __inline__
VOID
__outbyte (
  UINT16  Port,
  UINT8   Value
  )
{
  __asm__ __volatile__ ("outb %0, %1" : : "a" (Value), "d" (Port));
}

#endif

// ============================================================================
// Embedded Binary Data (PCIe Configuration Tables and GUIDs)
// ============================================================================

///
/// GUID storage block at 0xCE0 in .rdata.
/// Contains four GUIDs used by the driver.
///
static CONST UINT8  GuidBlock[0x80] = {
  //
  // [0x00] EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_GUID
  // {2F707EBB-4A1A-11D4-9A38-0090273FC14D}
  //
  0xBB, 0x7E, 0x70, 0x2F, 0x1A, 0x4A, 0xD4, 0x11,
  0x9A, 0x38, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D,
  //
  // [0x10] DebugLib / EFI_HOB_LIST_GUID
  // {36232936-0E76-31C8-A13A-3AF2FC1C3932}
  //
  0x36, 0x29, 0x23, 0x36, 0x76, 0x0E, 0xC8, 0x31,
  0xA1, 0x3A, 0x3A, 0xF2, 0xFC, 0x1C, 0x39, 0x32,
  //
  // [0x20] UBA_LIGHTNING_RIDGE_EXEC_B1_BOARD_TYPE_PROTOCOL_GUID
  // {E03E0D46-5263-4845-B0A4-58D57B3177E2}
  //
  0x46, 0x0D, 0x3E, 0xE0, 0x63, 0x52, 0x45, 0x48,
  0xB0, 0xA4, 0x58, 0xD5, 0x7B, 0x31, 0x77, 0xE2,
  //
  // [0x30] UBA registration GUID (same as the board-type GUID)
  //
  0x4C, 0xF2, 0x39, 0x77, 0xD7, 0x93, 0xD4, 0x11,
  0x9A, 0x3A, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D,
  //
  // [0x40] EFI_PCI_ENUMERATION_COMPLETE_GUID / EFI_HOB_LIST_GUID
  // {7739F24C-93D7-11D4-9A3A-0090273FC14D}
  //
  0x4C, 0xF2, 0x39, 0x77, 0xD7, 0x93, 0xD4, 0x11,
  0x9A, 0x3A, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D,
  //
  // [0x50-0x7F] Padding (zeros)
  //
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};

///
/// OpROM update protocol interface data block (at 0xF00 in original image).
/// This structure serves as the registered protocol interface.
/// It starts with a signature/header ("PBDS") followed by callbacks.
///
/// Layout:
///   +0x00: 0x0000000142445350 = "PBDS" signature + version 1
///   +0x08: 0x000000000000048C = total config size (1164 bytes)
///   +0x10: Callback: OpromGetConfigA
///   +0x18: Callback: OpromGetConfigB
///   +0x20: Callback: OpromGetConfigC
///   +0x28: Callback: OpromSetSlotNumber
///   +0x30: Null terminator
///
static CONST UINT8  OpromUpdateProtocolData[0x38] = {
  0x50, 0x42, 0x44, 0x53,  // "PBDS" signature
  0x01, 0x00, 0x00, 0x00,  // Version = 1
  0x8C, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  // Size = 0x048C
  // Function pointers (offsets 0x10-0x28) - set to 0 in data section;
  // in the actual binary these are relocation entries that get fixed up
  // by the DXE dispatcher to point to the actual function addresses.
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  // Null terminator
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};

///
/// PCIe configuration table A (at address 0xD20 in original image).
/// Contains PCIe root port and device configuration entries.
///
static CONST UINT8  PcieConfigTableA[0xE6];  // Content documented below

///
/// PCIe configuration table B (at address 0xD60 in original image).
/// 6 entries of OpROM update PCIe config data.
///
static CONST UINT8  PcieConfigTableB[0x150]; // Content documented below

///
/// PCIe configuration table C (at address 0xE60 in original image).
/// 10 entries of OpROM update PCIe config data.
///
static CONST UINT8  PcieConfigTableC[0x70];  // Content documented below

///
/// Slot configuration table (at address 0xED1 in original image).
/// 8 entries, each 4 bytes, defining PCIe slot BDF addresses.
///
static CONST UINT8  PcieSlotConfigTable[0x20] = {
  0x03, 0x00, 0x00, 0x00,  // Slot 0: B=0x03 D=0x00 F=0x00
  0x01, 0x80, 0x00, 0x00,  // Slot 1: B=0x01 D=0x80 F=0x00 (Dev=16, Func=0)
  0x02, 0x80, 0x00, 0x02,  // Slot 2: B=0x02 D=0x80 F=0x00 extra=0x02
  0x03, 0x80, 0x00, 0x00,  // Slot 3: B=0x03 D=0x80 F=0x00
  0x02, 0x00, 0x00, 0x00,  // Slot 4: B=0x02 D=0x00 F=0x00
  0x01, 0x00, 0x00, 0x05,  // Slot 5: B=0x01 D=0x00 F=0x00 extra=0x05
  0x1C, 0x00, 0x00, 0x00,  // Slot 6: B=0x1C D=0x00 F=0x00 (extended)
  0x03, 0x80, 0x00, 0x9C,  // Slot 7: B=0x03 D=0x80 F=0x00 extra=0x9C
};

// ============================================================================
// Global Variables (.data segment)
// ============================================================================

///
/// Cached pointer to the OpROM update protocol interface.
/// Resolved once by GetDebugLibProtocol() and reused thereafter.
///
static VOID   *gOpromUpdateProtocol = NULL;

///
/// Cached pointer to the HOB (Hand-Off Block) list.
/// Resolved once by GetHobList() and reused thereafter.
///
VOID   *gHobList = NULL;

// ============================================================================
// Callback Implementations
// ============================================================================

/**
 * Callback: GetConfigA.
 *
 * Returns a pointer to PCIe configuration table A. This table describes
 * the primary PCIe root port and bridge configuration for the platform.
 *
 * @param[out] ConfigData  Pointer to receive the table address.
 *
 * @return EFI_SUCCESS (always returns 0).
 */
EFI_STATUS
OpromGetConfigA (
  OUT VOID  **ConfigData
  )
{
  *ConfigData = (VOID *)PcieConfigTableA;
  return EFI_SUCCESS;
}

/**
 * Callback: GetConfigB.
 *
 * Returns a pointer to PCIe configuration table B and its entry count.
 * Table B contains 6 PCIe device configuration entries.
 *
 * @param[out] ConfigData   Pointer to receive the table address.
 * @param[out] EntryCount   Number of entries (6).
 *
 * @return EFI_SUCCESS (always returns 0).
 */
EFI_STATUS
OpromGetConfigB (
  OUT VOID    **ConfigData,
  OUT UINT32  *EntryCount
  )
{
  *ConfigData = (VOID *)PcieConfigTableB;
  *EntryCount = 6;
  return EFI_SUCCESS;
}

/**
 * Callback: GetConfigC.
 *
 * Returns a pointer to PCIe configuration table C and its entry count.
 * Table C contains 10 PCIe device configuration entries.
 *
 * @param[out] ConfigData   Pointer to receive the table address.
 * @param[out] EntryCount   Number of entries (10).
 *
 * @return EFI_SUCCESS (always returns 0).
 */
EFI_STATUS
OpromGetConfigC (
  OUT VOID    **ConfigData,
  OUT UINT32  *EntryCount
  )
{
  *ConfigData = (VOID *)PcieConfigTableC;
  *EntryCount = 10;
  return EFI_SUCCESS;
}

/**
 * Callback: SetPcieSlotNumber.
 *
 * Clears the PCIe slot number to 0 for OpROM binding. On the Lightning
 * Ridge EXEC B1 platform, this function reports slot number 0 and logs
 * a debug message indicating that the slot number callback was invoked.
 *
 * @param[out] SlotNumber  Set to 0 (no slot binding).
 *
 * @return EFI_SUCCESS (always returns 0).
 */
EFI_STATUS
OpromSetSlotNumber (
  OUT UINT8  *SlotNumber
  )
{
  *SlotNumber = 0;

  //
  // Log debug message: "[UBA]:SetPcieSlotNumber callback - %d\n"
  // with the slot number value (always 0 on this platform).
  //
  DebugPrint (DEBUG_INFO, "[UBA]:SetPcieSlotNumber callback - %d\n", 0);

  return EFI_SUCCESS;
}

// ============================================================================
// Protocol Resolution
// ============================================================================

/**
 * Locates the DebugLib protocol interface and initializes the OpROM update
 * callback table.
 *
 * This function performs the following:
 *   1. Allocates pool memory for the OpROM update protocol interface.
 *   2. Initializes the interface with the four callback pointers.
 *   3. Locates the DebugLib protocol via gBS->LocateProtocol() using the
 *      DebugLib GUID (36232936-0E76-31C8-A13A-3AF2FC1C3932).
 *   4. Caches the result in gOpromUpdateProtocol.
 *
 * The DebugLib protocol interface is a structure with functions at offsets
 * 0x00 and 0x08 (relative to the interface pointer).
 *   - Offset 0x00: Debug output function
 *   - Offset 0x08: Debug assertion handler
 *
 * If gBS->LocateProtocol() fails, gOpromUpdateProtocol is set to NULL.
 *
 * @return Pointer to the OpROM update protocol interface, or NULL on failure.
 */
VOID *
GetDebugLibProtocol (
  VOID
  )
{
  //
  // Check if protocol was already resolved.
  //
  if (gOpromUpdateProtocol != NULL) {
    return gOpromUpdateProtocol;
  }

  //
  // Allocate pool for the protocol interface.
  // The pool type used corresponds to memory type 31 (implementation-specific
  // allocation type for UBA protocol interfaces).
  // AllocatePool signature: (PoolType, Size, Buffer).
  //
  (*gBS->AllocatePool)(31, sizeof(OPROM_UPDATE_PROTOCOL_INTERFACE), &gOpromUpdateProtocol);

  //
  // Initialize the protocol interface with callback pointers.
  // The interface is initialized by setting up the internal callback table
  // with the four platform-specific functions.
  //
  ((OPROM_UPDATE_PROTOCOL_INTERFACE *)gOpromUpdateProtocol)->GetConfigA       = OpromGetConfigA;
  ((OPROM_UPDATE_PROTOCOL_INTERFACE *)gOpromUpdateProtocol)->GetConfigB       = OpromGetConfigB;
  ((OPROM_UPDATE_PROTOCOL_INTERFACE *)gOpromUpdateProtocol)->GetConfigC       = OpromGetConfigC;
  ((OPROM_UPDATE_PROTOCOL_INTERFACE *)gOpromUpdateProtocol)->SetPcieSlotNumber = OpromSetSlotNumber;

  //
  // Locate the DebugLib protocol using gBS->LocateProtocol().
  // If the protocol cannot be located (e.g., not yet installed), set the
  // cached pointer to NULL so the caller can handle gracefully.
  // LocateProtocol signature: (Protocol, Registration, Interface).
  //
  if (EFI_ERROR((*gBS->LocateProtocol)(&GuidBlock[0x20], NULL, &gOpromUpdateProtocol))) {
    gOpromUpdateProtocol = NULL;
  }

  return gOpromUpdateProtocol;
}

// ============================================================================
// Debug Output
// ============================================================================

/**
 * Debug print function.
 *
 * Resolves the DebugLib protocol via GetDebugLibProtocol() and calls its
 * debug output function if the current debug level matches the requested
 * severity mask.
 *
 * Debug level determination:
 *   The platform type is determined by reading CMOS register 0x4B:
 *     1. Read CMOS index 0x4B (preserving bit 7 - NMI disable bit).
 *     2. If the value is > 3, use the value directly.
 *     3. If the value is 0, fall back to MMIO register 0xFDAF0490
 *        (read bit 1, OR with 1).
 *     4. Platform type = (value - 1).
 *        - Type 1: Lightning Ridge EXEC B1 (enables DEBUG_INFO output)
 *        - Other types: Different platform variant
 *
 * Debug output is only emitted when:
 *   - A valid DebugLib protocol interface is available, AND
 *   - The platform type matches, AND
 *   - The ErrorLevel mask matches (DEBUG_INFO = 0x80000000)
 *
 * @param[in] ErrorLevel  Debug severity mask (e.g., DEBUG_INFO = 0x80000000).
 * @param[in] Format      Printf-style format string.
 * @param[in] ...         Variable arguments for the format string.
 *
 * @return Result from the DebugLib output function, or 0 on failure.
 */
UINTN
EFIAPI
DebugPrint (
  IN UINTN       ErrorLevel,
  IN CONST CHAR8 *Format,
  ...
  )
{
  VOID    *Protocol;
  UINTN   Result;
  UINT8   CmosValue;
  UINT8   PlatformType;
  UINTN   ErrorMask;

  Result = 0;

  //
  // Resolve the DebugLib/OpROM protocol interface.
  //
  Protocol = GetDebugLibProtocol ();
  if (Protocol == NULL) {
    return Result;
  }

  //
  // Determine platform type from CMOS register 0x4B.
  // Read the CMOS register using port I/O instructions.
  //
  CmosValue = __inbyte (RTC_INDEX_PORT);
  __outbyte (RTC_INDEX_PORT, (CmosValue & ~DEBUG_CMOS_MASK) | CMOS_DEBUG_LEVEL_REGISTER);
  PlatformType = __inbyte (RTC_DATA_PORT);

  //
  // Validate platform type. If > 3, use as-is.
  // If == 0, fall back to MMIO register at 0xFDAF0490.
  //
  if (PlatformType > 3) {
    // PlatformType is valid as-is
  } else if (PlatformType == 0) {
    PlatformType = (*(volatile UINT8 *)BOARD_CONFIG_MMIO_ADDR & 2) | 1;
  } else {
    // Platform type 1..3 = valid
  }

  //
  // Check if the platform type matches the Lightning Ridge EXEC B1 type.
  // (PlatformType - 1) is computed; if <= 0xFD, the type is valid.
  //
  if ((PlatformType - 1) <= 0xFD) {
    //
    // Build the error mask for this platform:
    // - Type 1 (Lightning Ridge): ErrorMask = 0x80000004
    // - Type 2 (other):            ErrorMask = 0x80000006
    //
    if (PlatformType == OPROM_PLATFORM_TYPE_LIGHTNING_RIDGE) {
      ErrorMask = 0x80000004;
    } else {
      ErrorMask = 0x80000006;
    }

    //
    // If the requested ErrorLevel matches the platform's error mask,
    // call the DebugLib protocol's output function (at offset 0x00).
    //
    if ((ErrorMask & ErrorLevel) != 0) {
      //
      // Invoke the debug output function.
      // Parameters: Protocol, ErrorLevel, Format.
      // Note: The original binary passes a va_list as the 4th argument by
      // directly pointing to the stack location after Format. In the actual
      // UEFI DebugLib implementation, this is the DebugVPrint function.
      //
      ((UINTN (*)(VOID *, UINTN, CONST CHAR8 *))Protocol)(Protocol, ErrorLevel, Format);
    }
  }

  return Result;
}

// ============================================================================
// Assertion Handler
// ============================================================================

/**
 * ASSERT assertion failure handler.
 *
 * Called when a runtime assertion fails. This function is invoked by the
 * ASSERT() macro when its condition evaluates to FALSE.
 *
 * The handler resolves the DebugLib protocol and calls its assertion
 * handler function at offset 0x08 in the protocol interface.
 *
 * @param[in] FileName     Source file name where the assertion failed.
 * @param[in] LineNumber   Line number of the assertion.
 * @param[in] Description  Description of the assertion that failed.
 *
 * @return 0 if the DebugLib protocol is not available, otherwise the
 *         return value from the protocol's assertion handler.
 */
UINTN
DebugAssert (
  IN CONST CHAR8  *FileName,
  IN UINTN        LineNumber,
  IN CONST CHAR8  *Description
  )
{
  VOID   *Protocol;
  UINTN  Result;

  Result = 0;

  //
  // Resolve the DebugLib protocol interface.
  //
  Protocol = GetDebugLibProtocol ();
  if (Protocol != NULL) {
    //
    // Call the assertion handler function at offset 0x08 in the
    // protocol interface. The assertion handler is accessed via pointer
    // arithmetic on the raw protocol interface.
    //
    { UINTN (*AssertFn)(VOID *, CONST CHAR8 *, UINTN, CONST CHAR8 *);
      AssertFn = *(UINTN (**)(VOID *, CONST CHAR8 *, UINTN, CONST CHAR8 *))((UINT8 *)Protocol + 8);
      Result = AssertFn (Protocol, FileName, LineNumber, Description);
    }
  }

  return Result;
}

// ============================================================================
// HOB List Management
// ============================================================================

/**
 * Locates the HOB (Hand-Off Block) list from the UEFI System Table's
 * configuration table array.
 *
 * Iterates through the configuration table array looking for a table whose
 * VendorGuid matches EFI_HOB_LIST_GUID. The HOB list contains system
 * initialization information passed from PEI to DXE, including PCI resource
 * allocation data.
 *
 * The function performs the following:
 *   1. Checks the cached gHobList first; if already resolved, returns it.
 *   2. Iterates through SystemTable->ConfigurationTable[] entries.
 *   3. For each entry, calls IsHobListGuid() to check if the VendorGuid matches.
 *   4. When found, caches the table pointer in gHobList.
 *   5. If not found and no configuration tables exist, triggers an assertion
 *      via DebugAssert() with the message "!EFI_ERROR (Status)".
 *   6. If gHobList remains NULL after the search, triggers an assertion
 *      with the message "mHobList != ((void *) 0)".
 *
 * Results are cached globally in gHobList so subsequent calls are fast.
 *
 * @param[in] ImageHandle  The driver image handle (not directly used).
 *
 * @return Pointer to the HOB list, or NULL if the HOB list GUID was not
 *         found in the system configuration table.
 */
VOID *
GetHobList (
  IN EFI_HANDLE  ImageHandle
  )
{
  UINTN                 Index;
  UINTN                 TableCount;
  EFI_CONFIGURATION_TABLE *ConfigTable;

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

  //
  // Initialize and iterate through configuration tables.
  //
  gHobList = NULL;
  TableCount = gST->NumberOfTableEntries;
  ConfigTable = gST->ConfigurationTable;

  if (TableCount > 0) {
    //
    // Walk the configuration table array.
    // Each entry is 24 bytes (sizeof(EFI_CONFIGURATION_TABLE)).
    //
    for (Index = 0; Index < TableCount; Index++) {
      //
      // Check if this table's VendorGuid matches EFI_HOB_LIST_GUID.
      //
      if (IsHobListGuid (NULL, &ConfigTable[Index].VendorGuid)) {
        gHobList = ConfigTable[Index].Table;
        break;
      }
    }
  }

  //
  // If no HOB list was found, trigger assertion failure.
  //
  if (gHobList == NULL) {
    DebugPrint (DEBUG_INFO, "\nASSERT_EFI_ERROR (Status = %r)\n", 0x800000000000000EULL);
    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;
}

/**
 * Compares a GUID against the EFI_HOB_LIST_GUID.
 *
 * Performs the comparison as two 64-bit unaligned reads:
 *   Compare first 8 bytes of the GUID against the expected first half
 *   (stored at GuidBlock + 0x10, i.e., the second GUID in the block which
 *    is EFI_HOB_LIST_GUID).
 *   Compare second 8 bytes of the GUID against the expected second half
 *   (stored at GuidBlock + 0x18).
 *
 * Both halves must match for the GUID to be considered equal.
 *
 * @param[in] ImageHandle  Unused parameter (for compatibility with entry point
 *                         signature; value is ignored).
 * @param[in] GuidPtr      Pointer to the 16-byte EFI_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;
  UINT64  ExpectedFirstHalf;
  UINT64  ExpectedSecondHalf;

  //
  // Read the expected GUID halves from the GUID storage block.
  // The EFI_HOB_LIST_GUID is stored at GuidBlock + 0x10.
  //
  ExpectedFirstHalf  = ReadUnaligned64 (&GuidBlock[0x10]);
  ExpectedSecondHalf = ReadUnaligned64 (&GuidBlock[0x18]);

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

  //
  // Both halves must match.
  //
  return (GuidFirstHalf == ExpectedFirstHalf) && (GuidSecondHalf == ExpectedSecondHalf);
}

// ============================================================================
// Slot Population Detection
// ============================================================================

/**
 * Checks whether a given PCI bus/device/function address falls within any
 * of the 8 configured PCIe slot address ranges.
 *
 * For each slot entry in the slot configuration table, the function reads
 * two PCI configuration space registers (offsets 0x19 = BIST, 0x1A = Header
 * Type) via the EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL. It then checks whether the
 * target BdfAddress falls between the BIST value (lower bound) and Header
 * Type value (upper bound), inclusive.
 *
 * The PCI address for each slot is constructed from the slot configuration
 * table bytes:
 *   slot_pci_addr = (prev_entry_byte3 << 8) | (entry_byte0 << 16) | (entry_byte1 << 24)
 *
 * Where entry_byte0 = Bus, entry_byte1 = Device, prev_entry_byte3 = continuation.
 * This format matches the EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS encoding:
 *   Bits [31:24] = Extended Register
 *   Bits [23:16] = Register offset
 *   Bits [15:11] = Function
 *   Bits [10:7]  = Device
 *   Bits [6:0]   = Bus
 *
 * @param[in] BdfAddress  The PCI Bus/Device/Function address to check.
 * @param[in] Reserved    Reserved parameter (not used on this platform).
 *
 * @retval TRUE   The target BDF address is within one of the configured slots.
 * @retval FALSE  The target BDF address is not within any configured slot,
 *                or EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL is not available.
 */
BOOLEAN
IsPcieSlotConfigured (
  IN UINT64  BdfAddress,
  IN UINT32  Reserved
  )
{
  EFI_STATUS  Status;
  UINTN       Index;
  CONST UINT8 *SlotEntry;
  UINT8       SlotBus;
  UINT8       SlotDevice;
  UINT8       SlotPrevExtra;
  UINT64      SlotBdfAddress;
  VOID        *RootBridgeIo;
  UINT8       BistValue;
  UINT8       HeaderTypeValue;
  BOOLEAN     Found;

  Found = FALSE;

  //
  // Locate the EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL.
  // The protocol GUID is stored at GuidBlock + 0x00.
  //
  Status = (*gBS->LocateProtocol) (&GuidBlock[0x00], NULL, (VOID **)&RootBridgeIo);
  if (EFI_ERROR (Status)) {
    return FALSE;
  }

  //
  // Iterate through 8 slot entries in the slot configuration table.
  // Each entry is 4 bytes.
  //
  SlotEntry = PcieSlotConfigTable;
  for (Index = 0; Index < OPROM_UPDATE_MAX_SLOTS; Index++) {
    //
    // Extract slot PCI address from the configuration table:
    //   SlotPrevExtra = byte3 of PREVIOUS entry (or 0 for first entry,
    //     since the byte before the table is 0)
    //   SlotBus       = byte0 of CURRENT entry
    //   SlotDevice    = byte1 of CURRENT entry
    //
    // If Index == 0, SlotPrevExtra is read from the byte preceding
    // the slot configuration table (which is 0x00).
    //
    if (Index == 0) {
      SlotPrevExtra = 0;
    } else {
      SlotPrevExtra = SlotEntry[-2];  // byte3 of previous entry
    }
    SlotBus    = SlotEntry[0];  // byte0
    SlotDevice = SlotEntry[1];  // byte1

    //
    // Check if this slot's BDF matches the target BDF address.
    // If the slot's bit in 'flags' is not set, skip this slot.
    //
    if ((Reserved & (1 << Index)) == 0) {
      //
      // Build the PCI config space address for this slot:
      //   SlotBdfAddress = (SlotPrevExtra << 8) | (SlotBus << 16) | (SlotDevice << 24)
      //
      SlotBdfAddress = ((UINT64)SlotPrevExtra << 8)
                     | ((UINT64)SlotBus << 16)
                     | ((UINT64)SlotDevice << 24);

      //
      // Read registers at offsets 0x19 (BIST) and 0x1A (Header Type)
      // via the PciRootBridgeIo protocol. The PCI Read function is at
      // offset 0x38 (56) within the protocol interface structure.
      // Parameters: This (RootBridgeIo), Width=0 (Uint8), Address, Count, Buffer.
      //
      ((EFI_STATUS (*)(VOID *, UINT32, UINT64, UINT32, VOID *))
        *(UINT64 *)((UINT8 *)RootBridgeIo + 56)) (
        RootBridgeIo,
        0,                              // EfiPciWidthUint8
        SlotBdfAddress | PCI_REG_BIST,
        1,
        &BistValue
        );
      ((EFI_STATUS (*)(VOID *, UINT32, UINT64, UINT32, VOID *))
        *(UINT64 *)((UINT8 *)RootBridgeIo + 56)) (
        RootBridgeIo,
        0,                              // EfiPciWidthUint8
        SlotBdfAddress | PCI_REG_HEADER_TYPE,
        1,
        &HeaderTypeValue
        );

      //
      // Check if the target BDF address falls within the range defined by
      // this slot. The BIST value is the lower bound and Header Type is
      // the upper bound.
      //
      if (BdfAddress >= BistValue && BdfAddress <= HeaderTypeValue) {
        Found = TRUE;
        break;
      }
    }

    //
    // Advance to the next slot entry (4 bytes per entry).
    //
    SlotEntry += 4;
  }

  return Found;
}

// ============================================================================
// Utility Functions
// ============================================================================

/**
 * Reads an unaligned 64-bit value from memory.
 *
 * Performs a NULL pointer check. If Buffer is NULL, an assertion is
 * triggered via DebugAssert() with the message "Buffer != ((void *) 0)".
 *
 * On valid input, reads 8 bytes from the given address and returns them
 * as a 64-bit integer. The read is performed as a direct memory access
 * (the platform supports unaligned access).
 *
 * @param[in] Buffer  Pointer to the 8-byte value to read. Must not be NULL.
 *
 * @return The 64-bit value at the given memory address.
 */
UINT64
ReadUnaligned64 (
  IN CONST VOID  *Buffer
  )
{
  //
  // Validate Buffer is not NULL.
  //
  if (Buffer == NULL) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\BaseLib\\Unaligned.c",
      192,
      "Buffer != ((void *) 0)"
      );
  }

  //
  // Read 8 bytes from the buffer as a 64-bit value.
  // Cast to volatile to prevent compiler optimizations from re-ordering
  // or combining the access.
  //
  return *(CONST UINT64 *)Buffer;
}

// ============================================================================
// Module Entry Point
// ============================================================================

/**
 * Module entry point for OpromUpdateDxeLightningRidgeEXECB1.
 *
 * DXE driver entry point called by the UEFI Boot Manager when this driver
 * image is loaded and dispatched.
 *
 * Entry point flow:
 *   1. Cache global variables: ImageHandle, SystemTable, BootServices,
 *      RuntimeServices (standard UefiBootServicesTableLib initialization).
 *   2. Locate HOB list via GetHobList() to ensure PCI enumeration data
 *      is available.
 *   3. Log debug banner: "UBA:OpromUpdate-TypeLightningRidgeEXECB1\n"
 *      via DebugPrint().
 *   4. Locate the UBA Lightning Ridge EXEC B1 board-type protocol via
 *      gBS->LocateProtocol() using GUID E03E0D46-5263-4845-B0A4-58D57B3177E2.
 *   5. Call the protocol's registration function (at offset 0x10 within
 *      the protocol interface) to register the OpROM update configuration.
 *   6. The registration function receives:
 *      - The protocol interface
 *      - The registration GUID (E03E0D46-...)
 *      - The OpROM update protocol data block
 *      - Size: 0x30 bytes (48)
 *
 * @param[in] ImageHandle  The firmware-allocated handle for this driver image.
 * @param[in] SystemTable  A pointer to the EFI System Table.
 *
 * @return EFI_SUCCESS           Registration succeeded.
 * @return EFI_INVALID_PARAMETER ImageHandle or SystemTable is NULL.
 * @return EFI_NOT_FOUND         The UBA board-type protocol could not be located.
 * @return Other                 Registration via the board-type protocol failed.
 */
EFI_STATUS
EFIAPI
_ModuleEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;
  VOID        *UbaProtocol;
  UINT64      RegistrationResult;

  //
  // Validate input parameters.
  //
  if (ImageHandle == NULL) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
      51,
      "gImageHandle != ((void *) 0)"
      );
  }
  if (SystemTable == NULL) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
      57,
      "gST != ((void *) 0)"
      );
  }
  if (SystemTable->BootServices == NULL) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
      63,
      "gBS != ((void *) 0)"
      );
  }
  if (SystemTable->RuntimeServices == NULL) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\UefiRuntimeServicesTableLib\\UefiRuntimeServicesTableLib.c",
      47,
      "gRT != ((void *) 0)"
      );
  }

  //
  // Cache global pointers.
  //
  gImageHandle = ImageHandle;
  gST          = SystemTable;
  gBS          = SystemTable->BootServices;
  gRT          = SystemTable->RuntimeServices;

  //
  // Locate HOB list (ensures PCI enumeration data is available).
  // Initializes gHobList for use by the UBA framework.
  //
  GetHobList (ImageHandle);

  //
  // Log debug banner indicating this is the OpROM Update driver for
  // the Lightning Ridge EXEC B1 platform.
  //
  DebugPrint (DEBUG_INFO, "UBA:OpromUpdate-TypeLightningRidgeEXECB1\n");

  //
  // Locate the UBA Lightning Ridge EXEC B1 board-type protocol.
  // The protocol GUID is stored at GuidBlock + 0x40.
  //
  UbaProtocol = NULL;
  Status = (*gBS->LocateProtocol) (&GuidBlock[0x40], NULL, &UbaProtocol);

  //
  // If the UBA protocol was found, register the OpROM update configuration.
  // The registration function is at offset 0x10 in the protocol interface.
  //
  if (!EFI_ERROR (Status)) {
    Status = ((EFI_STATUS (*)(VOID *, EFI_GUID *, VOID *, UINT32))
              ((UBA_OPROM_UPDATE_BOARD_TYPE_PROTOCOL *)UbaProtocol)->RegisterOpromUpdateConfig) (
               UbaProtocol,
               (EFI_GUID *)&GuidBlock[0x40],  // Same GUID as protocol
               (VOID *)OpromUpdateProtocolData,
               48  // 0x30 bytes
               );

    return Status;
  }

  return Status;
}