/**
 * IioCfgUpdateDxeNeonCityFPGA.c
 *
 * IIO Configuration Update DXE Driver for the NeonCityFPGA platform.
 *
 * This driver is part of the Lenovo ThinkSystem HR650X UBA (Unified Board
 * Architecture) framework. It registers platform-specific IIO (Integrated IO)
 * configuration tables during the DXE phase.
 *
 * The IIO subsystem on Intel Xeon Scalable (Purley) platforms controls:
 *   - PCIe root port configuration and lane partitioning
 *   - IIO stack-to-port mappings
 *   - Link speed and width configurations
 *   - Bifurcation settings
 *
 * On the NeonCityFPGA platform, the IIO configuration is provided via
 * four PIIO tables (PIIO = Platform IIO):
 *   - Table 0: 30 entries, entry size 228 bytes (GPIO/strapping config?)
 *   - Table 1: 27 entries, entry size 180 bytes
 *   - Table 2: 24 entries, entry size 156 bytes
 *   - Table 3: 27 entries, entry size 204 bytes
 *
 * Each PIIO table is identified by a unique GUID and registered with the
 * UBA framework via the board-type protocol.
 *
 * Binary details:
 *   File: IioCfgUpdateDxeNeonCityFPGA.efi
 *   SHA256: 4ed7a6c35bc04e5dd3f7e59e6b388411b1dfcff0f2b3fabea6d37769bd0e16eb
 *   PE index: 0011
 *   .text:    0x2C0 - 0x7C0 (0x500 bytes, 9 functions)
 *   .rdata:   0x7C0 - 0xB80 (0x3C0 bytes, string literals)
 *   .data:    0xB80 - 0x1080 (0x500 bytes, GUIDs + PIIO tables)
 *   seg004:   0x1080 - 0x10E0 (0x60 bytes, unused/zeroed)
 *   .xdata:   0x10E0 - 0x1140 (0x60 bytes, zeroed)
 *   .reloc:   0x1140 - 0x1180 (0x40 bytes, base relocations)
 *   GAP:      0x1180 - 0x2000 (0xE80 bytes, BSS/alignment)
 */

#include "IioCfgUpdateDxeNeonCityFPGA.h"

/*===========================================================================
 * GUID Const Data (.rdata section, embedded)
 *===========================================================================*/

/*
 * These GUIDs are embedded as const data in the .data section of the binary.
 * They are declared here for clarity; the actual byte sequences match the
 * GUID definitions in the header.
 */

/*===========================================================================
 * Static Global Variables (.data section, 0xB80-0x1080)
 *===========================================================================*/

/*
 * Memory Layout of .data section:
 *
 * Offset  Contents                        Size   Description
 * ------  --------                        ----   -----------
 * 0xB80   IIO_CFG_TABLE_0_GUID            16     GUID for table 0
 * 0xB90   DEBUG_LIB_PROTOCOL_GUID          16     DebugLib protocol GUID
 * 0xBA0   IIO_CFG_TABLE_1_GUID            16     GUID for table 1
 * 0xBB0   UBA_BOARD_TYPE_PROTOCOL_GUID    16     UBA board-type protocol GUID
 * 0xBC0   IIO_CFG_TABLE_2_GUID            16     GUID for table 2
 * 0xBD0   EFI_HOB_LIST_GUID (first half)  16     1st 8 bytes: GUID, 2nd 8 bytes: padding
 *            Note: bytes at 0xBD0 match the upper 8 bytes of EFI_HOB_LIST_GUID
 *            and bytes at 0xBD8 match the lower 8 bytes -- they are stored
 *            split for the IsGuidEqual comparison.
 * 0xBE0   IIO_CFG_TABLE_3_GUID            16     GUID for table 3
 *          + BoardConfigFlags            (24)   Platform config attribute bytes
 * 0xC08   PIIO Table 0 header + descriptors  (varies) Headers + 30 entry descriptors
 * 0xDE8   PIIO Table 1 header (shared with table 2)
 * 0xE18   PIIO Table 2 header
 * 0xFF8   PIIO Table 3 header
 * 0x1048  gST                             8      EFI_SYSTEM_TABLE pointer
 * 0x1050  gBS                             8      EFI_BOOT_SERVICES pointer
 * 0x1058  gImageHandle                    8      EFI_HANDLE
 * 0x1060  gRT                             8      EFI_RUNTIME_SERVICES pointer
 * 0x1068  mDebugProtocol                  8      Cached DebugLib protocol pointer
 * 0x1070  mHobList                        8      Cached HOB list pointer
 * 0x1078  mCmosDebugLevel                 1      Cached CMOS debug level byte
 */

/*===========================================================================
 * Static Variable Declarations (mapped at runtime)
 *===========================================================================*/

/* UEFI boot service globals */
EFI_SYSTEM_TABLE     *gST;         /* Address: 0x1048 */
EFI_BOOT_SERVICES    *gBS;         /* Address: 0x1050 */
EFI_HANDLE            gImageHandle;/* Address: 0x1058 */
EFI_RUNTIME_SERVICES *gRT;         /* Address: 0x1060 */

/* Internal cached pointers */
static DEBUG_LIB_PROTOCOL  *mDebugProtocol; /* Address: 0x1068 */
static VOID                *mHobList;       /* Address: 0x1070 */
static UINT8                mCmosDebugLevel;/* Address: 0x1078 */

/*===========================================================================
 * PIIO Configuration Tables (embedded in .data section)
 *===========================================================================*/

/*
 * The 4 PIIO tables and their associated GUIDs are embedded as constant
 * data in the binary at the following locations:
 *
 *  GUID        PIIO Header  Table Description
 *  ----        -----------  -----------------
 *  0xB80       0xC08        Table 0: 30 entries x 228 bytes
 *  0xBA0       0xE18        Table 1: 24 entries x 156 bytes
 *  0xBC0       0xDE8        Table 2: 27 entries x 180 bytes
 *  0xBE0       0xFF8        Table 3: 27 entries x 204 bytes
 *
 * The GUIDs and headers are separated in memory but paired by the
 * RegisterIioConfigTables() function.
 */

/*===========================================================================
 * Helper Functions
 *===========================================================================*/

/**
 * Returns EFI_SUCCESS (thunk function).
 *
 * Present at 0x4F8 in the binary. Simply returns zero. No xrefs from
 * within the module, suggesting this may be reachable via the PE
 * export table or referenced by the linker.
 *
 * @return EFI_SUCCESS always.
 */
EFI_STATUS
ReturnSuccess(
  VOID
  )
{
  return EFI_SUCCESS;
}

/**
 * Reads a UINT64 from a pointer with NULL assertion.
 *
 * Implements the pattern from UEFI BaseLib's ReadUnaligned64().
 * Located at 0x78C in the binary.
 *
 * @param[in] Buffer  Pointer to read from. Must not be NULL.
 * @return The 64-bit value at the given address.
 */
UINT64
ReadUnaligned64(
  IN CONST UINT64  *Buffer
  )
{
  if (Buffer == NULL) {
    DebugAssert("e:\\hs\\MdePkg\\Library\\BaseLib\\Unaligned.c", 192,
                "Buffer != ((void *) 0)");
  }
  return *Buffer;
}

/**
 * Compares two GUIDs using 64-bit unaligned reads.
 *
 * Optimized comparison that avoids calling CompareGuid(). Splits each
 * GUID into two 64-bit halves and compares them directly. Used for
 * scanning the system configuration table for the HOB list GUID.
 *
 * Note: The first GUID (a1) is compared against a config table entry
 * at offset 0 (first 8 bytes are compared to unk_BD0, second 8 bytes
 * to unk_BD8).
 *
 * Located at 0x71C in the binary.
 *
 * @param[in] Guid1  Pointer to the target GUID (from config table scan).
 * @param[in] Guid2  Pointer to the reference GUID (EFI_HOB_LIST_GUID).
 * @return TRUE if GUIDs match, FALSE otherwise.
 */
BOOLEAN
IsGuidEqual(
  IN EFI_GUID  *Guid1,
  IN EFI_GUID  *Guid2
  )
{
  UINT64  *Guid1Parts = (UINT64 *)Guid1;
  UINT64  *Guid2Parts = (UINT64 *)Guid2;

  /*
   * Compare first 8 bytes of each GUID.
   * Compares unk_BD0 (first half of EFI_HOB_LIST_GUID) against
   * Guid2->Data1..Data2..Data3 (first 8 bytes of the config table entry).
   */
  if (ReadUnaligned64((UINT64 *)&unk_BD0) != ReadUnaligned64(Guid2Parts)) {
    return FALSE;
  }

  /*
   * Compare second 8 bytes of each GUID.
   * Compares unk_BD8 (second half of EFI_HOB_LIST_GUID) against
   * Guid2->Data4 (bytes 8-15 of the config table entry).
   */
  if (ReadUnaligned64((UINT64 *)&unk_BD8) != ReadUnaligned64(Guid2Parts + 1)) {
    return FALSE;
  }

  return TRUE;
}

/**
 * Resolves and caches the DebugLib protocol interface.
 *
 * Before calling LocateProtocol, performs a UEFI environment validation
 * check by allocating and freeing a zero-byte buffer. If the returned
 * pointer is <= 0x10, the boot services are considered non-functional
 * and NULL is returned (the canary pattern detects a broken/missing
 * UEFI environment).
 *
 * On success, caches the protocol pointer in mDebugProtocol and returns it.
 *
 * Located at 0x4FC in the binary.
 *
 * @return Pointer to DEBUG_LIB_PROTOCOL, or NULL on failure.
 */
DEBUG_LIB_PROTOCOL *
GetDebugProtocol(
  VOID
  )
{
  DEBUG_LIB_PROTOCOL  *Protocol;
  VOID                *Buffer;
  EFI_STATUS           Status;

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

  /*
   * Environment validation: allocate and free a zero-size buffer.
   * On a functional UEFI, AllocatePool(31, 0, &Buffer) returns a
   * valid pointer (not NULL, not <= 0x10). A broken or stubbed
   * environment returns a very small or NULL pointer.
   */
  Status = gBS->AllocatePool(31, 0, &Buffer);
  if (!EFI_ERROR(Status)) {
    gBS->FreePool(Buffer);
  }

  if ((UINTN)Buffer <= 0x10) {
    return NULL;
  }

  /* Locate the DebugLib protocol */
  Status = gBS->LocateProtocol(
    &DEBUG_LIB_PROTOCOL_GUID,
    NULL,
    (VOID **)&Protocol
    );

  if (EFI_ERROR(Status)) {
    Protocol = NULL;
  }

  mDebugProtocol = Protocol;
  return Protocol;
}

/**
 * Debug assertion handler.
 *
 * Calls the DebugLib protocol's DebugAssert function pointer at
 * offset +0x08 of the protocol interface. If the DebugLib protocol
 * is not available, silently ignores the assertion.
 *
 * Located at 0x604 in the binary.
 *
 * @param[in] FileName     Source file name where assertion occurred.
 * @param[in] LineNumber   Line number of the assertion.
 * @param[in] Description  Assertion condition description string.
 */
VOID
DebugAssert(
  IN CONST CHAR8  *FileName,
  IN UINT64        LineNumber,
  IN CONST CHAR8  *Description
  )
{
  DEBUG_LIB_PROTOCOL  *Protocol;

  Protocol = GetDebugProtocol();
  if (Protocol != NULL) {
    Protocol->DebugAssert(FileName, LineNumber, Description);
  }
}

/**
 * Debug print function with CMOS-level verbosity filtering.
 *
 * Behavior:
 * 1. Resolves the DebugLib protocol if not already cached.
 * 2. Reads CMOS register 0x4B via RTC ports 0x70/0x71 to get
 *    the board-specific debug level.
 * 3. If CMOS level > 3, clamps to the raw value.
 *    If CMOS level == 0, falls back to MMIO 0xFDAF0490 to read
 *    the hardware debug level.
 * 4. Maps debug level to an ErrorLevel filter mask:
 *      Level 1 -> ErrorLevel = 0x80000004 (EFI_D_ERROR | ...)
 *      Otherwise -> ErrorLevel = 0x80000006
 * 5. Calls the DebugLib DebugPrint if the message's ErrorLevel
 *    passes the filter.
 *
 * Located at 0x57C in the binary.
 *
 * @param[in] ErrorLevel  Debug message error level.
 * @param[in] Format      Format string.
 * @param[in] ...         Variable arguments.
 * @return 1 if message was printed, 0 if filtered or no protocol.
 */
UINT8
DebugPrint(
  IN UINT64        ErrorLevel,
  IN CONST CHAR8   *Format,
  ...
  )
{
  DEBUG_LIB_PROTOCOL  *Protocol;
  UINT8                DebugLevel;
  UINT8                CmosIndex;
  UINT64               FilterMask;
  VA_LIST              Args;

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

  /* Read CMOS register 0x4B for debug level */
  __outbyte(0x70, (__inbyte(0x70) & 0x80) | 0x4B); /* Preserve NMI bit */
  DebugLevel = __inbyte(0x71);

  /*
   * Normalize debug level:
   * - If Level > 3, keep as-is (possibly a special encoding)
   * - If Level == 0, read board-level debug from MMIO 0xFDAF0490
   *   (a board-specific register on NeonCityFPGA)
   */
  if (DebugLevel > 3) {
    /* Keep raw value */
  } else if (DebugLevel == 0) {
    /*
     * Fallback: read MMIO register at 0xFDAF0490.
     * This register provides the board's debug configuration.
     * Only the low 2 bits are used: (value & 2) | 1
     * gives a valid debug level of 1 or 3.
     */
    DebugLevel = (*(volatile UINT8 *)0xFDAF0490 & 2) | 1;
  }

  /*
   * Map debug level to filter mask:
   *   Level 1 -> 0x80000004  (EFI_D_ERROR = 0x80000000 | 0x4)
   *   Other  -> 0x80000006  (EFI_D_ERROR | EFI_D_WARN = 0x80000000 | 0x6)
   *
   * Note: 0x80000000 is the high bit set for error-level messages.
   * The lower bits correspond to UEFI debug level masks:
   *   0x00000001 = EFI_D_INIT
   *   0x00000002 = EFI_D_WARN
   *   0x00000004 = EFI_D_ERROR
   */
  if (DebugLevel == 1) {
    FilterMask = 0x80000004;
  } else {
    FilterMask = 0x80000006;
  }

  /* Check if the message passes the filter */
  if ((FilterMask & ErrorLevel) != 0) {
    VA_START(Args, Format);
    Protocol->DebugPrint(ErrorLevel, Format, Args);
    VA_END(Args);
    return 1;
  }

  return 0;
}

/**
 * Locates and caches the HOB (Hand-Off Block) list pointer.
 *
 * Scans the SystemTable->ConfigurationTable[] array for the
 * EFI_HOB_LIST_GUID entry. Each configuration table entry is
 * 0x18 (24) bytes:
 *   +0x00: EFI_GUID  VendorGuid (16 bytes)
 *   +0x10: VOID      *VendorTable (8 bytes)
 *
 * The number of entries is at SystemTable->NumberOfTableEntries
 * (offset +0x68 from SystemTable base).
 * The ConfigurationTable pointer is at SystemTable + 0x70.
 *
 * On success, stores the HOB list pointer in mHobList and returns it.
 * On failure (no matching entry), triggers an assertion with
 * EFI_NOT_FOUND status code (0x800000000000000E).
 *
 * Located at 0x644 in the binary.
 *
 * @param[in] SystemTable  Pointer to EFI_SYSTEM_TABLE (not directly used;
 *                         the function reads from the global gST instead).
 * @return Pointer to the HOB list, or NULL if not found.
 */
VOID *
GetHobList(
  IN EFI_SYSTEM_TABLE  *SystemTable  /* UNUSED - uses gST global */
  )
{
  UINTN   Index;
  UINT64  EntryCount;
  UINT8   *ConfigTableBase;
  EFI_GUID  *EntryGuid;

  if (mHobList != NULL) {
    return mHobList;
  }

  mHobList = NULL;

  /* Get number of configuration table entries from SystemTable+0x68 */
  EntryCount = *(UINT64 *)((UINT8 *)gST + 0x68);

  if (EntryCount == 0) {
    goto Error_NotFound;
  }

  /* Get ConfigurationTable pointer from SystemTable+0x70 */
  ConfigTableBase = *(UINT8 **)((UINT8 *)gST + 0x70);

  /* Scan for EFI_HOB_LIST_GUID */
  for (Index = 0; Index < EntryCount; Index++) {
    EntryGuid = (EFI_GUID *)(ConfigTableBase + (Index * 24));

    if (IsGuidEqual(NULL, EntryGuid)) {
      /*
       * Found matching entry.
       * The HOB list pointer is at offset +0x10 within the
       * configuration table entry.
       */
      mHobList = *(VOID **)(ConfigTableBase + (Index * 24) + 16);
      break;
    }
  }

  if (mHobList == NULL) {
    goto Error_NotFound;
  }

  return mHobList;

Error_NotFound:
  DebugPrint(0x80000000, "\nASSERT_EFI_ERROR (Status = %r)\n", EFI_NOT_FOUND);
  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 mHobList;
}

/**
 * Registers all IIO configuration tables with the UBA framework.
 *
 * This function performs the main work of the driver:
 * 1. Prints the "UBA:IioCfgUpdate-TypeNeonCityFPGA" banner via DebugPrint.
 * 2. Locates the UBA board-type protocol via gBS->LocateProtocol().
 * 3. Calls RegisterIioCfg() for each of the 4 IIO configuration tables,
 *    passing the table GUID, PIIO header, and a size of 48 bytes.
 *
 * Each table registration passes exactly the PIIO_TABLE_HEADER (48 bytes)
 * as the configuration data. The UBA framework uses the header to locate
 * and interpret the entry descriptors that follow in memory.
 *
 * Located at 0x43C in the binary.
 *
 * @return EFI_SUCCESS on success, or the error status from LocateProtocol.
 */
EFI_STATUS
RegisterIioConfigTables(
  VOID
  )
{
  EFI_STATUS           Status;
  UBA_IIO_CFG_PROTOCOL *UbaProtocol = NULL;

  /* Print initialization banner */
  DebugPrint(0x80000000, "UBA:IioCfgUpdate-TypeNeonCityFPGA\n");

  /* Locate the UBA board-type protocol */
  Status = gBS->LocateProtocol(
    &UBA_BOARD_TYPE_PROTOCOL_GUID,
    NULL,
    (VOID **)&UbaProtocol
    );

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

  /*
   * Register all 4 IIO configuration tables.
   * Each call passes:
   *   - The 16-byte GUID identifying the config table
   *   - The 48-byte PIIO_TABLE_HEADER describing the table layout
   *   - Size = 48 (the PIIO header size; the full table including
   *     entry descriptors extends beyond this in memory but is
   *     accessed through the TotalSize/DataOffset fields in the header)
   */

  /* Table 0: GUID at 0xB80, PIIO header at 0xC08, 30 entries, esize=228 */
  UbaProtocol->RegisterIioCfg(
    UbaProtocol,
    (EFI_GUID *)&IIO_CFG_TABLE_0_GUID,
    (PIIO_TABLE_HEADER *)0xC08,
    48
    );

  /* Table 1: GUID at 0xBA0, PIIO header at 0xE18, 24 entries, esize=156 */
  UbaProtocol->RegisterIioCfg(
    UbaProtocol,
    (EFI_GUID *)&IIO_CFG_TABLE_1_GUID,
    (PIIO_TABLE_HEADER *)0xE18,
    48
    );

  /* Table 2: GUID at 0xBC0, PIIO header at 0xDE8, 27 entries, esize=180 */
  UbaProtocol->RegisterIioCfg(
    UbaProtocol,
    (EFI_GUID *)&IIO_CFG_TABLE_2_GUID,
    (PIIO_TABLE_HEADER *)0xDE8,
    48
    );

  /* Table 3: GUID at 0xBE0, PIIO header at 0xFF8, 27 entries, esize=204 */
  UbaProtocol->RegisterIioCfg(
    UbaProtocol,
    (EFI_GUID *)&IIO_CFG_TABLE_3_GUID,
    (PIIO_TABLE_HEADER *)0xFF8,
    48
    );

  return EFI_SUCCESS;
}

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

/**
 * Module entry point.
 *
 * Called by the DXE dispatcher during driver initialization. Performs:
 * 1. Caches gImageHandle (asserts if NULL).
 * 2. Caches gST (asserts if NULL).
 * 3. Extracts and caches gBS from SystemTable->BootServices (asserts if NULL).
 * 4. Extracts and caches gRT from SystemTable->RuntimeServices (asserts if NULL).
 * 5. Calls GetHobList() to locate and cache the HOB list pointer.
 * 6. Calls RegisterIioConfigTables() to register all 4 PIIO tables.
 *
 * Located at 0x390 in the binary.
 *
 * @param[in] ImageHandle  The firmware allocated handle for the EFI image.
 * @param[in] SystemTable  A pointer to the EFI System Table.
 * @return The result of RegisterIioConfigTables().
 */
EFI_STATUS
EFIAPI
ModuleEntryPoint(
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE *SystemTable
  )
{
  /* Validate and cache ImageHandle */
  gImageHandle = (UINT64)ImageHandle;
  if (ImageHandle == NULL) {
    DebugAssert("e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\"
                "UefiBootServicesTableLib.c", 51,
                "gImageHandle != ((void *) 0)");
  }

  /* Validate and cache SystemTable */
  gST = (UINT64)SystemTable;
  if (SystemTable == NULL) {
    DebugAssert("e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\"
                "UefiBootServicesTableLib.c", 57,
                "gST != ((void *) 0)");
  }

  /* Extract and cache BootServices from SystemTable->BootServices (+0x60) */
  gBS = (UINT64)SystemTable->BootServices;
  if (gBS == NULL) {
    DebugAssert("e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\"
                "UefiBootServicesTableLib.c", 63,
                "gBS != ((void *) 0)");
  }

  /* Extract and cache RuntimeServices from SystemTable->RuntimeServices (+0x58) */
  gRT = (UINT64)SystemTable->RuntimeServices;
  if (gRT == NULL) {
    DebugAssert("e:\\hs\\MdePkg\\Library\\UefiRuntimeServicesTableLib\\"
                "UefiRuntimeServicesTableLib.c", 47,
                "gRT != ((void *) 0)");
  }

  /* Locate and cache the HOB list */
  GetHobList(SystemTable);

  /* Register all IIO configuration tables */
  return RegisterIioConfigTables();
}