/** @file
  IioCfgUpdateDxeLightningRidgeEXECB3 -- IIO Configuration Update DXE Driver
  for the LightningRidge EX EC B3 platform.

  This UEFI DXE driver configures Integrated IO (IIO) PCIe bus topology for
  the LightningRidge EX EC B3 platform. It operates as a UBA (Universal BIOS
  Architecture) board-type driver, registering IIO configuration data tables
  via the UBA protocol's AddConfig interface.

  == Architecture ==
  1. ModuleEntryPoint caches UEFI globals (gImageHandle, gST, gBS, gRT)
  2. GetHobList() resolves the HOB list via SystemTable->ConfigurationTable
  3. IioCfgUpdateEntryPoint() emits the platform banner and locates the
     UBA IIO configuration protocol
  4. Four IIO PCIe topology configuration tables are registered, each keyed
     by a distinct GUID and described by a "PIIO" header (48 bytes):
       Table 1: {0x36232936-0E76-31C8-A13A-3AF2FC1C3932}
       Table 2: {0x0F722F2A-650F-448A-ABB7-04EECD75BB30}
       Table 3: {0xE03E0D46-5263-4845-B0A4-58D57B3177E2}
       Table 4: {0xEBD11A00-8C5C-4F71-BB9E-5394032B01F4}

  == Binary Info ==
  File:    IioCfgUpdateDxeLightningRidgeEXECB3.efi
  Index:   30 of 427 PE files in HR650X BIOS
  Size:    3776 bytes (0xEC0)
  SHA256:  5698c437729140af878cef496aaeeaaa2e1222aca5686eec31ff9c9d6dd47d4d
  MD5:     f38f5e2647ef28abb5bc010b20b0fe2d
  Arch:    X64
  PDB:     IioCfgUpdateDxeLightningRidgeEXECB3.pdb

  Copyright (c) 2023-2026, Dell Inc. and/or its affiliates. All rights reserved.
  SPDX-License-Identifier: BSD-2-Clause-Patent
**/

#include "IioCfgUpdateDxeLightningRidgeEXECB3.h"

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

///
/// UBA IIO Configuration Protocol GUID
/// Image offset: 0xBC0 (16 bytes)
///
STATIC CONST EFI_GUID  mUbaIioCfgProtocolGuid = UBA_IIO_CFG_PROTOCOL_GUID;

///
/// IIO Table 1 GUID
/// Image offset: 0xBD0 (16 bytes)
///
STATIC CONST EFI_GUID  mIioCfgTable1Guid = IIO_CFG_TABLE_1_GUID;

///
/// IIO Table 2 GUID
/// Image offset: 0xBE0 (16 bytes)
///
STATIC CONST EFI_GUID  mIioCfgTable2Guid = IIO_CFG_TABLE_2_GUID;

///
/// IIO Table 3 GUID
/// Image offset: 0xBF0 (16 bytes)
///
STATIC CONST EFI_GUID  mIioCfgTable3Guid = IIO_CFG_TABLE_3_GUID;

///
/// IIO Table 4 GUID
/// Image offset: 0xC00 (16 bytes)
///
STATIC CONST EFI_GUID  mIioCfgTable4Guid = IIO_CFG_TABLE_4_GUID;

///
/// EFI HOB List GUID
/// Image offset: 0xC10 (16 bytes)
/// Used by IsHobListGuid() to identify the HOB list in ConfigurationTable[].
///
STATIC CONST EFI_GUID  mEfiHobListGuid = EFI_HOB_LIST_GUID;

///
/// UBA Debug Library Protocol GUID
/// Image offset: 0xBD0 (duplicate of IIO Table 1 GUID -- the decompiler
/// references 0xBD0 as the debug lib protocol GUID in sub_510).
///
STATIC CONST EFI_GUID  mUbaDebugLibProtocolGuid = UBA_DEBUG_LIB_PROTOCOL_GUID;

/*==============================================================================
 * Global Variable Definitions (.data segment)
 *============================================================================*/

///
/// gImageHandle -- UEFI image handle for this driver.
/// Image offset: 0xDB0
///
EFI_HANDLE  gImageHandle = NULL;

///
/// gST -- UEFI System Table pointer.
/// Image offset: 0xDA0
///
EFI_SYSTEM_TABLE  *gST = NULL;

///
/// gBS -- UEFI Boot Services pointer.
/// Image offset: 0xDA8
///
EFI_BOOT_SERVICES  *gBS = NULL;

///
/// gRT -- UEFI Runtime Services pointer.
/// Image offset: 0xDB8
///
EFI_RUNTIME_SERVICES  *gRT = NULL;

///
/// mUbaDebugProtocol -- Cached UBA debug protocol pointer.
/// Image offset: 0xDC0
/// Resolved lazily by LocateUbaDebugProtocol().
///
VOID  *mUbaDebugProtocol = NULL;

///
/// mHobList -- Cached HOB list pointer.
/// Image offset: 0xDC8
/// Resolved lazily by GetHobList().
///
VOID  *mHobList = NULL;

/*==============================================================================
 * IIO Configuration Table Header (PIIO)
 *
 * This 48-byte structure at image offset 0xD30 describes the IIO PCIe
 * topology configuration data block:
 *
 *   Signature:  "PIIO"
 *   Version:    1
 *   EntrySize:  0xD60 (3424 bytes per entry)
 *   EntryCount: 60 (0x3C)
 *   TableSize:  0x50C (1292 bytes)
 *   CfgOffset:  0xC30 (3120 bytes from header)
 *   CfgSize:    0xFC (252 bytes of config data)
 *
 * The configuration data itself is small (0xFC = 252 bytes) but the entry
 * size is large (0xD60) -- this suggests the entries define broad IIO stack
 * parameters per PCIe root port with sparse encoding, or the entry size
 * includes padding for hardware register layout alignment.
 *============================================================================*/

STATIC CONST IIO_CFG_TABLE_HEADER  mIioCfgHeader = {
  .Signature  = { 'P', 'I', 'I', 'O' },
  .Version    = 1,
  .EntrySize  = 0xD60,
  .EntryCount = 60,
  .TableSize  = 0x50C,
  .CfgOffset  = 0xC30,
  .CfgSize    = 0xFC,
};

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

/**
  UEFI DXE driver entry point for IioCfgUpdateDxeLightningRidgeEXECB3.

  Called by the DXE Dispatcher after all dependencies are satisfied.
  Performs the following initialization sequence:

  1. Cache ImageHandle and SystemTable pointers into global variables.
  2. Validate non-NULL: ImageHandle, SystemTable, BootServices,
     RuntimeServices (triggers UBA assert on failure).
  3. Resolve the HOB list via GetHobList().
  4. Dispatch the IIO configuration update via IioCfgUpdateEntryPoint().

  @param[in] ImageHandle   Handle for this driver image.
  @param[in] SystemTable   Pointer to the UEFI system table.

  @return EFI_SUCCESS            IIO configuration registered successfully.
  @return EFI_UNSUPPORTED/...    Protocol Locate or AddConfig failure.
**/
EFI_STATUS
EFIAPI
ModuleEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;

  //
  // Step 1: Cache UEFI standard pointers globally.
  //
  gImageHandle = ImageHandle;
  gST          = SystemTable;

  //
  // Step 2: Validate non-NULL and cache boot/runtime services.
  //
  if (ImageHandle == NULL) {
    UbaDebugPrint (
      0x80000000,
      "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c"
      );
    UbaDebugAssert (
      "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
      51,
      "gImageHandle != ((void *) 0)"
      );
  }

  gBS = SystemTable->BootServices;
  if (SystemTable == NULL) {
    UbaDebugPrint (
      0x80000000,
      "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c"
      );
    UbaDebugAssert (
      "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
      57,
      "gST != ((void *) 0)"
      );
  }

  if (gBS == NULL) {
    UbaDebugPrint (
      0x80000000,
      "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c"
      );
    UbaDebugAssert (
      "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
      63,
      "gBS != ((void *) 0)"
      );
  }

  gRT = SystemTable->RuntimeServices;
  if (gRT == NULL) {
    UbaDebugPrint (
      0x80000000,
      "e:\\hs\\MdePkg\\Library\\UefiRuntimeServicesTableLib\\UefiRuntimeServicesTableLib.c"
      );
    UbaDebugAssert (
      "e:\\hs\\MdePkg\\Library\\UefiRuntimeServicesTableLib\\UefiRuntimeServicesTableLib.c",
      47,
      "gRT != ((void *) 0)"
      );
  }

  //
  // Step 3: Resolve the HOB list (cached in mHobList).
  //
  GetHobList ();

  //
  // Step 4: Dispatch the IIO configuration update.
  //
  Status = IioCfgUpdateEntryPoint ();
  return Status;
}

/**
  Entry point for IIO configuration table registration.

  Registers four IIO PCIe topology configuration data tables through the
  UBA protocol's AddConfig interface. Each registration provides a table
  GUID and the same IIO_CFG_TABLE_HEADER (PIIO struct) with data size 48.

  Sequence:
  1. Emit the platform identification banner via UbaDebugPrint.
  2. Locate the UBA IIO configuration protocol by GUID
     {0x6FE6C559-4F35-4111-98E1-332A251512F3} via gBS->LocateProtocol().
  3. Call the protocol's AddConfig function (vtable index 2, offset 0x10)
     four times, once per table GUID, passing the PIIO header and size 48.
  4. Return immediately on first failure.

  @return EFI_SUCCESS             All four tables registered successfully.
  @return EFI_UNSUPPORTED         Protocol GUID not found.
  @return EFI_INVALID_PARAMETER   AddConfig call failed for a table.
**/
EFI_STATUS
IioCfgUpdateEntryPoint (
  VOID
  )
{
  EFI_STATUS  Status;
  VOID        *UbaProtocol;          ///< Located UBA protocol interface

  //
  // Step 1: Initialize output variable and emit platform banner.
  //
  UbaProtocol = NULL;

  UbaDebugPrint (
    0x80000000,
    "UBA:IioCfgUpdate-TypeLightningRidgeEXECB3\n"
    );

  //
  // Step 2: Locate the UBA IIO configuration protocol.
  //
  Status = gBS->LocateProtocol (
                  &mUbaIioCfgProtocolGuid,
                  NULL,
                  &UbaProtocol
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Step 3: Register the four IIO PCIe topology configuration tables.
  // Each call passes the same PIIO header structure (48 bytes) but keyed
  // by a different table GUID.
  //
  // The AddConfig function is at vtable index 2 (offset 0x10):
  //   EFI_STATUS (*AddConfig)(
  //     VOID *This,
  //     CONST EFI_GUID *ConfigGuid,
  //     CONST VOID *ConfigData,
  //     UINTN ConfigDataSize
  //     );
  //

  //
  // Table 1: Primary IIO topology
  //
  Status = ((EFI_STATUS (*)(VOID *, CONST EFI_GUID *, CONST VOID *, UINTN))
            (*((UINT64 **)UbaProtocol) + 2)) (
             UbaProtocol,
             &mIioCfgTable1Guid,
             &mIioCfgHeader,
             sizeof (IIO_CFG_TABLE_HEADER)
             );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Table 2: Secondary IIO topology
  //
  Status = ((EFI_STATUS (*)(VOID *, CONST EFI_GUID *, CONST VOID *, UINTN))
            (*((UINT64 **)UbaProtocol) + 2)) (
             UbaProtocol,
             &mIioCfgTable2Guid,
             &mIioCfgHeader,
             sizeof (IIO_CFG_TABLE_HEADER)
             );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Table 3: Tertiary IIO topology
  //
  Status = ((EFI_STATUS (*)(VOID *, CONST EFI_GUID *, CONST VOID *, UINTN))
            (*((UINT64 **)UbaProtocol) + 2)) (
             UbaProtocol,
             &mIioCfgTable3Guid,
             &mIioCfgHeader,
             sizeof (IIO_CFG_TABLE_HEADER)
             );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Table 4: Quaternary IIO topology
  //
  Status = ((EFI_STATUS (*)(VOID *, CONST EFI_GUID *, CONST VOID *, UINTN))
            (*((UINT64 **)UbaProtocol) + 2)) (
             UbaProtocol,
             &mIioCfgTable4Guid,
             &mIioCfgHeader,
             sizeof (IIO_CFG_TABLE_HEADER)
             );

  return Status;
}

/**
  Locates the UBA debug protocol (lazy init with caching).

  On first call, performs a pool size sanity check (allocate 31 bytes, if
  only <= 0x10 bytes available, returns NULL) then calls gBS->LocateProtocol()
  using the UBA debug protocol GUID. The result is cached in mUbaDebugProtocol.

  @return Pointer to the UBA debug protocol interface, or NULL if unavailable.
**/
VOID *
LocateUbaDebugProtocol (
  VOID
  )
{
  UINT64  PoolSize;     ///< Pool allocation for size check

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

  //
  // Perform pool size sanity check.
  // Allocate and immediately free a 31-byte buffer. If the returned pool
  // size is <= 0x10, the system is in a state where protocol resolution
  // would also fail, so bail out early.
  //
  gBS->AllocatePool (EfiBootServicesData, 31, (VOID **)&PoolSize);
  gBS->FreePool ((VOID *)PoolSize);

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

  //
  // Locate the UBA debug protocol.
  //
  mUbaDebugProtocol = NULL;
  gBS->LocateProtocol (
         &mUbaDebugLibProtocolGuid,
         NULL,
         &mUbaDebugProtocol
         );

  return mUbaDebugProtocol;
}

/**
  Platform-aware debug print via the UBA debug protocol.

  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 IIO variants):
      Debug mask = 0x80000006

  If CMOS register 0x4B reads as 0, falls back to MMIO read at physical
  address 0xFDAF0490 (GPIO/strapping register), extracting bit 1 ORed
  with 1 to derive the platform type.

  Debug output is only emitted when (ErrorLevel & DebugMask) != 0.

  @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 if debug output was emitted or successfully suppressed.
  @return 0              Debug protocol not available.
**/
UINTN
EFIAPI
UbaDebugPrint (
  IN UINTN       ErrorLevel,
  IN CONST CHAR8 *Format,
  ...
  )
{
  VA_LIST                   VaList;
  VOID                      *Protocol;
  UINT8                     CmosValue;
  UINTN                     PlatformType;
  UINTN                     DebugMask;

  //
  // Resolve the UBA debug protocol (lazy init).
  //
  Protocol = LocateUbaDebugProtocol ();
  if (Protocol == NULL) {
    return 0;
  }

  //
  // Read CMOS register 0x4B to determine the board platform type.
  // Access sequence:
  //   1. Read CMOS index register (I/O port 0x70) to preserve NMI bit
  //   2. Write index with (NMI_disabled | 0x4B) to select register 0x4B
  //   3. Read data from CMOS data register (I/O port 0x71)
  //
  CmosValue = IoRead8 (0x70);
  IoWrite8 (0x70, (CmosValue & 0x80) | 0x4B);
  PlatformType = IoRead8 (0x71);

  //
  // Determine platform type from CMOS value.
  // If value is > 3 and equals 0, fall back to MMIO strapping register.
  //
  if (PlatformType > 3) {
    if (PlatformType == 0) {
      PlatformType = (*(volatile UINT8 *)(UINTN)0xFDAF0490 & 2) | 1;
    }
  }

  //
  // Select debug mask based on platform type.
  // Type 1 = LightningRidge, others use a broader mask.
  //
  if (PlatformType == 1) {
    DebugMask = 0x80000004;
  } else {
    DebugMask = 0x80000006;
  }

  //
  // Check if the error level matches the platform debug mask.
  //
  if ((DebugMask & ErrorLevel) == 0) {
    return 4;
  }

  //
  // Call the protocol's DebugPrint function (vtable offset 0x00).
  //
  VA_START (VaList, Format);
  return ((UINTN (*)(UINTN, CONST CHAR8 *, VA_LIST))
          (*(UINT64 *)Protocol)) (
           ErrorLevel,
           Format,
           VaList
           );
}

/**
  ASSERT failure handler via the UBA debug protocol.

  Called when an ASSERT() condition evaluates to FALSE. Routes to the
  UBA debug protocol's assertion handler at vtable offset 0x08.

  @param[in] FileName     Full path to the source file containing the assert.
  @param[in] LineNumber   Line number of the assertion.
  @param[in] Description  Text description of the failing assertion condition.
**/
VOID
EFIAPI
UbaDebugAssert (
  IN CONST CHAR8  *FileName,
  IN UINTN        LineNumber,
  IN CONST CHAR8  *Description
  )
{
  VOID  *Protocol;

  //
  // Resolve the UBA debug protocol (lazy init).
  //
  Protocol = LocateUbaDebugProtocol ();
  if (Protocol != NULL) {
    //
    // Call the protocol's assertion handler (vtable offset 0x08).
    //
    ((VOID (*)(CONST CHAR8 *, UINTN, CONST CHAR8 *))
     (*((UINT64 *)Protocol + 1))) (
      FileName,
      LineNumber,
      Description
      );
  }
}

/**
  Retrieves the HOB (Hand-Off Block) list pointer from the UEFI system table.

  Scans gST->ConfigurationTable[] for the entry whose VendorGuid matches
  EFI_HOB_LIST_GUID ({7739F24C-93D7-11D4-9A3A-0090273FC14D}). The associated
  VendorTable pointer is the HOB list. Result is cached in mHobList.

  If the GUID is not found in any configuration table entry, or if the
  resolved pointer is NULL, triggers UBA debug assertions.

  @return Pointer to the HOB list, or NULL if not found.
**/
VOID *
GetHobList (
  VOID
  )
{
  UINTN                   Index;
  EFI_CONFIGURATION_TABLE *ConfigTable;
  UINTN                   TableCount;

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

  //
  // Initialize cache to NULL before scanning.
  //
  mHobList    = NULL;
  TableCount  = gST->NumberOfTableEntries;
  ConfigTable = gST->ConfigurationTable;

  //
  // Scan the configuration table array for the HOB list GUID.
  //
  if (ConfigTable != NULL) {
    for (Index = 0; Index < TableCount; Index++) {
      if (IsHobListGuid (&ConfigTable[Index].VendorGuid)) {
        mHobList = (VOID *)ConfigTable[Index].VendorTable;
        break;
      }
    }
  }

  //
  // If the GUID was not found, trigger a debug assert.
  //
  if (mHobList == NULL) {
    UbaDebugPrint (
      0x80000000,
      "\nASSERT_EFI_ERROR (Status = %r)\n",
      0x800000000000000EULL    // EFI_NOT_FOUND
      );
    UbaDebugAssert (
      "e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
      54,
      "!EFI_ERROR (Status)"
      );
  }

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

  return mHobList;
}

/**
  Compares a GUID against EFI_HOB_LIST_GUID using 64-bit wide comparisons.

  Both the input GUID and the reference EFI_HOB_LIST_GUID are split into
  two 64-bit halves and compared via ReadUnaligned64().

  @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 GUID as two 64-bit halves.
  //
  return (ReadUnaligned64 (&mEfiHobListGuid) == ReadUnaligned64 (Guid))
      && (ReadUnaligned64 ((UINT8 *)&mEfiHobListGuid + 8)
            == ReadUnaligned64 ((UINT8 *)Guid + 8));
}

/**
  Reads a 64-bit value from a (possibly unaligned) memory address.

  Directly dereferences a 64-bit pointer. Asserts via UbaDebugAssert()
  if the pointer is NULL.

  @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
  )
{
  //
  // Validate buffer pointer is not NULL.
  //
  if (Buffer == NULL) {
    UbaDebugAssert (
      "e:\\hs\\MdePkg\\Library\\BaseLib\\Unaligned.c",
      192,
      "Buffer != ((void *) 0)"
      );
  }

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