/**
 * @file StaticSkuDataDxeNeonCityFPGA.c
 *
 * @brief Static SKU Data DXE driver for NeonCity FPGA platform.
 *
 * This driver installs three configuration tables into the ACPI
 * configuration table system:
 *   1. UMPT (Ubox/Memory topology)  -- 24 bytes at 0x4090
 *   2. PIRQ (PCI IRQ routing)       -- 16 bytes at 0x40B8
 *   3. ACPF (ACPI Platform SKU cfg) -- 16 bytes at 0x9000, referencing
 *      a 632-entry SKU_CONFIG_ENTRY table at 0x40E0
 *
 * The SKU data table maps platform device identifiers (like "PSYS",
 * "_SB_.CCT0".."_SB_.CCT8", "_SB_.CFH0".."_SB_.CFH8") to ACPI
 * namespace path strings. For NVDR (NVMe/Drive) entries, it maps
 * function-level registers (FXCD, FXST, FXIN, FXOU, FXBS, FXFH,
 * CENA, CFIS) across multiple NVMe controller instances (N000-N350).
 * For PCI root bridges, it maps FIX1-FIX8 and MCTL registers
 * (PC00-PC23, BR0-BR3, MCP0-MCP7, QRP0-RRP0-SRP0).
 */

#include "StaticSkuDataDxeNeonCityFPGA.h"

/*============================================================================
 * GUID Declarations
 *============================================================================*/

///
/// Protocol GUID to locate the ACPI configuration interface.
/// {E03E0D46-5263-4845-B0A4-58D57B3177E2}
///
STATIC CONST EFI_GUID mPlatformProtocolGuid = STATIC_SKU_DATA_PROTOCOL_GUID;

///
/// GUID for the HOB list in gST->ConfigurationTable.
/// {7739F24C-93D7-11D4-9A3A-0090273FC14D}
///
STATIC CONST EFI_GUID mHobListGuid = HOB_LIST_GUID;

///
/// GUID key for the UMPT (Ubox Platform Topology) ACPI table.
/// {0FF8A1CF-A0AB-4AC0-BFC9-34A78F68DD8A}
///
STATIC CONST EFI_GUID mUmptTableGuid = UMPT_TABLE_GUID;

///
/// GUID key for the PIRQ (PCI IRQ Routing) ACPI table.
/// {4C1F48A5-C976-4D90-9F03-8E9B1C327FCF}
///
STATIC CONST EFI_GUID mPirqTableGuid = PIRQ_TABLE_GUID;

///
/// GUID key for the ACPF (ACPI Platform SKU data) ACPI table.
/// {81129EF8-391D-4F63-AE99-58517EC077E3}
///
STATIC CONST EFI_GUID mAcpfTableGuid = ACPF_TABLE_GUID;

/*============================================================================
 * Global Variables (stored in .data section, 0xF520-0xF550)
 *============================================================================*/

///
/// @brief Cached ImageHandle from entry point.
///
EFI_HANDLE  gImageHandle = NULL;

///
/// @brief Cached SystemTable pointer from entry point.
///
EFI_SYSTEM_TABLE  *gSystemTable = NULL;

///
/// @brief Cached BootServices pointer (SystemTable->BootServices).
///
EFI_BOOT_SERVICES  *gBootServices = NULL;

///
/// @brief Cached RuntimeServices pointer (SystemTable->RuntimeServices).
///
EFI_RUNTIME_SERVICES  *gRuntimeServices = NULL;

///
/// @brief Cached platform protocol interface (lazy init at 0x4FC).
///
VOID  *gPlatformProtocol = NULL;

///
/// @brief Cached HOB list pointer (lazy init at 0x608).
///
VOID  *gHobList = NULL;


/*============================================================================
 * Static Data: SKU Variable Headers (in .data section)
 *============================================================================*/

///
/// @brief UMPT configuration header at 0x4090 (24 bytes).
///
/// Format: "UMPT" + Ver=1 + Ptr=0x9040 + NextPtr=0x9020 + GUID suffix.
///
STATIC CONST UMPT_VAR_BLOCK mUmptHeader = {
  { SIGNATURE_32('U','M','P','T'), 1, (UINT64)0x9040 },
  (UINT64)0x9020,
  (UINT64)0x0000000000000000    // Placeholder; actual GUID bytes follow
};

///
/// @brief PIRQ configuration header at 0x40B8 (16 bytes).
///
/// Format: "PIRQ" + Ver=1 + Ptr=0x90B8.
///
STATIC CONST SKU_VAR_HEADER mPirqHeader = {
  SIGNATURE_32('P','I','R','Q'), 1, (UINT64)0x90B8
};

///
/// @brief ACPF configuration header at 0x9000 (16 bytes).
///
/// Format: "ACPF" + Ver=1 + Ptr=0x40E0 (to the SKU_CONFIG_ENTRY table).
///
STATIC CONST SKU_VAR_HEADER mAcpfHeader = {
  SIGNATURE_32('A','C','P','F'), 1, (UINT64)0x40E0
};

///
/// @brief SKU data identifier at 0x4060 (first 16 bytes of .data).
///
/// GUID: {36232936-0E76-31C8-A13A-3AF2FC1C3932}
///
STATIC CONST EFI_GUID mSkuDataIdentifier = SKU_DATA_IDENTIFIER_GUID;

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

/**
 * @brief UEFI DXE driver entry point.
 *
 * Implements the standard UEFI driver entry point pattern:
 * 1. Caches ImageHandle, SystemTable, BootServices, RuntimeServices
 *    with NULL-check assertions.
 * 2. Locates the HOB list via GetHobList().
 * 3. Locates the platform-specific ACPI configuration protocol.
 * 4. Calls the protocol's InstallConfigurationTable three times to
 *    register UMPT, PIRQ, and ACPF table entries.
 *
 * @param[in] ImageHandle  The firmware-allocated handle for this driver.
 * @param[in] SystemTable  Pointer to the UEFI system table.
 * @return EFI_SUCCESS  The driver initialized successfully.
 * @return Other        Error from LocateProtocol or InstallConfigurationTable.
 */
EFI_STATUS
EFIAPI
StaticSkuDataDxeEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;
  VOID        *ProtocolInterface;  // v4 in decompilation

  // 1. Cache global pointers with NULL-check assertions
  gImageHandle = ImageHandle;
  PlatformAssert (__FILE__, __LINE__, "gImageHandle != ((void *) 0)");

  gSystemTable = SystemTable;
  PlatformAssert (__FILE__, __LINE__, "gST != ((void *) 0)");

  gBootServices = SystemTable->BootServices;
  PlatformAssert (__FILE__, __LINE__, "gBS != ((void *) 0)");

  gRuntimeServices = SystemTable->RuntimeServices;
  PlatformAssert (__FILE__, __LINE__, "gRT != ((void *) 0)");

  // 2. Locate the HOB list (for DXE services)
  GetHobList (ImageHandle);

  // 3. Print platform banner
  PlatformDebugPrint (0x80000000, "UBA:OpromUpdate-TypeLightningRidgeEXECB1\n");

  // 4. Locate the platform-specific ACPI configuration protocol
  //    Protocol GUID: {E03E0D46-5263-4845-B0A4-58D57B3177E2}
  //    v4+0x10 = InstallConfigurationTable function
  ProtocolInterface = NULL;
  Status = gBootServices->LocateProtocol (
                            (EFI_GUID *)&mPlatformProtocolGuid,
                            NULL,
                            &ProtocolInterface
                            );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  // 5. Install three ACPI configuration tables
  //
  //    The protocol interface has the following layout:
  //    +0x00: Reserved/Version
  //    +0x08: Assert function (used by PlatformAssert)
  //    +0x10: InstallConfigurationTable(GUID *Key, VOID *Data, UINTN Size)

  // Install UMPT table: 24 bytes of UMPT_VAR_BLOCK data
  //   Key GUID:  {0FF8A1CF-A0AB-4AC0-BFC9-34A78F68DD8A}
  //   Data at:   0x4090 (mUmptHeader)
  //   Size:      24 bytes
  Status = ((EFI_STATUS (EFIAPI *)(VOID *, EFI_GUID *, VOID *, UINTN))
            (*((VOID ***)ProtocolInterface))[2]) (
              ProtocolInterface,
              (EFI_GUID *)&mUmptTableGuid,
              (VOID *)&mUmptHeader,
              24
              );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  // Install PIRQ table: 16 bytes of PIRQ header
  //   Key GUID:  {4C1F48A5-C976-4D90-9F03-8E9B1C327FCF}
  //   Data at:   0x40B8 (mPirqHeader)
  //   Size:      16 bytes
  Status = ((EFI_STATUS (EFIAPI *)(VOID *, EFI_GUID *, VOID *, UINTN))
            (*((VOID ***)ProtocolInterface))[2]) (
              ProtocolInterface,
              (EFI_GUID *)&mPirqTableGuid,
              (VOID *)&mPirqHeader,
              16
              );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  // Install ACPF table: 16 bytes of SKU data header
  //   Key GUID:  {81129EF8-391D-4F63-AE99-58517EC077E3}
  //   Data at:   0x9000 (mAcpfHeader)
  //   Size:      16 bytes
  //   The DataPointer field (0x40E0) references the 632-entry
  //   SKU_CONFIG_ENTRY table.
  Status = ((EFI_STATUS (EFIAPI *)(VOID *, EFI_GUID *, VOID *, UINTN))
            (*((VOID ***)ProtocolInterface))[2]) (
              ProtocolInterface,
              (EFI_GUID *)&mAcpfTableGuid,
              (VOID *)&mAcpfHeader,
              16
              );

  return Status;
}


/*============================================================================
 * HOB List Locator
 *============================================================================*/

/**
 * @brief Locate and cache the HOB list pointer.
 *
 * Walks SystemTable->ConfigurationTable[] looking for an entry whose
 * VendorGuid matches HOB_LIST_GUID. The associated VendorTable pointer
 * is the HOB list. Cached in gHobList on success.
 *
 * @param[in] ImageHandle  Unused; passed as first arg to maintain
 *                         compatibility with the wrapper calling convention.
 * @return Pointer to the first HOB, or NULL if not found.
 */
VOID *
GetHobList (
  IN EFI_HANDLE  ImageHandle
  )
{
  UINTN  Index;

  // Guard: return cached value if already initialized
  if (gHobList != NULL) {
    return gHobList;
  }

  gHobList = NULL;

  // Walk the configuration table
  if (gSystemTable->NumberOfTableEntries > 0) {
    for (Index = 0; Index < gSystemTable->NumberOfTableEntries; Index++) {
      if (IsHobGuidMatch (
            (UINT64 *)&mHobListGuid,
            (UINT64 *)&gSystemTable->ConfigurationTable[Index].VendorGuid
            )) {
        // Matched the HOB list GUID; cache the vendor table pointer
        gHobList = gSystemTable->ConfigurationTable[Index].VendorTable;
        break;
      }
    }
  }

  // If not found, trigger debug assertion
  if (gHobList == NULL) {
    PlatformDebugPrint (0x80000000, "\nASSERT_EFI_ERROR (Status = %r)\n", 0x800000000000000EULL);
    PlatformAssert (__FILE__, __LINE__, "!EFI_ERROR (Status)");
  }

  return gHobList;
}


/*============================================================================
 * GUID Comparison Helpers
 *============================================================================*/

/**
 * @brief Compare two GUID values using 64-bit halves.
 *
 * Reads the first 8 bytes of each GUID and checks for equality.
 * Uses ReadUnaligned64 for safe access to potentially unaligned pointers.
 *
 * @param[in] Reference  Address of the first GUID (e.g. HOB_LIST_GUID).
 * @param[in] Target     Address of the second GUID (e.g. configuration table entry).
 * @return TRUE  if the first 8 bytes match.
 * @return FALSE if they differ.
 */
BOOLEAN
IsHobGuidMatch (
  IN UINT64  *Reference,
  IN UINT64  *Target
  )
{
  UINT64  RefLow;
  UINT64  RefHigh;
  UINT64  TgtLow;
  UINT64  TgtHigh;

  // Read first 64-bit half
  RefLow  = ReadUnaligned64 (Reference);
  TgtLow  = ReadUnaligned64 (Target);

  // Read second 64-bit half
  RefHigh = ReadUnaligned64 ((UINT64 *)((UINT8 *)Reference + 8));
  TgtHigh = ReadUnaligned64 ((UINT64 *)((UINT8 *)Target + 8));

  return (RefLow == TgtLow) && (RefHigh == TgtHigh);
}


/**
 * @brief Unaligned 64-bit read with NULL-check assertion.
 *
 * Returns *(UINT64 *)Buffer. Asserts if Buffer is NULL.
 * Based on BaseLib ReadUnaligned64.
 *
 * @param[in] Buffer  Pointer to read from (may be misaligned).
 * @return UINT64 value at Buffer.
 */
UINT64
ReadUnaligned64 (
  IN CONST VOID  *Buffer
  )
{
  PlatformAssert (__FILE__, __LINE__, "Buffer != ((void *) 0)");
  return *(const UINT64 *)Buffer;
}


/*============================================================================
 * Platform Protocol Access
 *============================================================================*/

/**
 * @brief Lazy-initialized platform protocol interface getter.
 *
 * On first call:
 * 1. Allocates 31 bytes from gBS->AllocatePool (boot services data type).
 * 2. Frees the pool immediately. If AllocatePool returned <= 0x10,
 *    returns NULL (pool probe guard).
 * 3. Locates the platform protocol via gBS->LocateProtocol.
 * 4. Caches the protocol pointer in gPlatformProtocol.
 *
 * On subsequent calls, returns the cached pointer directly.
 *
 * The pool probe (31 bytes) is a heuristic check to verify that the
 * boot services pool allocator is functional. If the allocation returned
 * an address <= 0x10, the allocator is considered unreliable and
 * LocateProtocol is skipped.
 *
 * @return Pointer to the platform protocol interface, or NULL if:
 *         - Pool probe failed (returned <= 0x10)
 *         - LocateProtocol failed
 */
VOID *
GetPlatformProtocol (
  VOID
  )
{
  UINT64  PoolProbe;
  UINT64  *ProtocolPtr;

  // Return cached value if already initialized
  if (gPlatformProtocol != NULL) {
    return gPlatformProtocol;
  }

  // Pool probe: allocate 31 bytes to check allocator health
  PoolProbe = (UINT64)gBootServices->AllocatePool (31);
  gBootServices->FreePool ((VOID *)PoolProbe);

  // If pool allocation returned a suspiciously low address, bail out
  if (PoolProbe <= 0x10) {
    return NULL;
  }

  // Locate the platform protocol
  gPlatformProtocol = NULL;
  gBootServices->LocateProtocol (
                   (EFI_GUID *)&mPlatformProtocolGuid,
                   NULL,
                   &gPlatformProtocol
                   );

  // On failure, clear the cached pointer
  ProtocolPtr = (UINT64 *)&gPlatformProtocol;
  if ((INT64)gBootServices->LocateProtocol (
          (EFI_GUID *)&mPlatformProtocolGuid,
          NULL,
          &gPlatformProtocol
          ) < 0) {
    *ProtocolPtr = 0;
  }

  return gPlatformProtocol;
}


/*============================================================================
 * Platform Debug / Assert Support
 *============================================================================*/

/**
 * @brief Platform debug print.
 *
 * Reads the platform type from RTC CMOS register 0x4B.
 * Based on the platform type:
 *   1 = Debug via route 0x80000004
 *   2+ = Debug via route 0x80000006
 *   Fallback 0 = hardware strap at 0xFDAF0490 bit 1, ORed with 1
 *
 * Routes the debug output through the protocol interface if the
 * debug route mask matches the DebugLevel parameter.
 *
 * @param[in] DebugLevel   0x80000000 for error messages.
 * @param[in] FormatString printf-style format.
 * @param[in] ...          Variable arguments.
 * @return TRUE if the debug message was dispatched.
 */
BOOLEAN
PlatformDebugPrint (
  IN UINT64       DebugLevel,
  IN CONST CHAR8  *FormatString,
  ...
  )
{
  VOID    *Protocol;
  UINT8   CmosReg;
  UINT8   PlatformType;
  UINT64  DebugRoute;

  Protocol = GetPlatformProtocol ();
  DebugRoute = 0;

  if (Protocol != NULL) {
    // Read RTC CMOS register 0x4B to determine platform type
    CmosReg = IoRead8 (0x70);
    IoWrite8 (0x70, (CmosReg & 0x80) | 0x4B);  // Select reg 0x4B, preserve NMI mask
    PlatformType = IoRead8 (0x71);

    if (PlatformType > 3) {
      // Platform type is already valid (>3), keep as-is
    }

    if (PlatformType == 0) {
      // Fallback: read hardware strap at 0xFDAF0490 bit 1, OR with 1
      PlatformType = (*(volatile UINT8 *)0xFDAF0490 & 2) | 1;
    }

    // Determine debug route based on platform type (minus 1 for 0-indexed check)
    if ((PlatformType - 1) <= 0xFD) {
      if (PlatformType == 1) {
        DebugRoute = 0x80000004;  // Standard debug route
      } else {
        DebugRoute = 0x80000006;  // Alternative debug route
      }
    }

    // If the debug route matches the requested level, dispatch to protocol
    if ((DebugRoute & DebugLevel) != 0) {
      // Protocol function 1 (offset 0x08) = DebugPrint
      ((VOID (EFIAPI *)(VOID *, CONST CHAR8 *, VA_LIST))
       (*((VOID ***)Protocol))[1]) (
         Protocol,
         FormatString,
         (VA_LIST)&FormatString + sizeof(CONST CHAR8 *)
         );
    }
  }

  return (BOOLEAN)(UINTN)Protocol;
}


/**
 * @brief Platform assert handler.
 *
 * Resolves the debug protocol and calls its assert handler
 * (function at offset 0x08 = function index 1 in the protocol).
 *
 * @param[in] FileName     Source file name where assertion failed.
 * @param[in] LineNumber   Line number of the failed assertion.
 * @param[in] AssertString Description of the failed assertion expression.
 */
VOID
PlatformAssert (
  IN CONST CHAR8  *FileName,
  IN UINTN        LineNumber,
  IN CONST CHAR8  *AssertString
  )
{
  VOID  *Protocol;

  Protocol = GetPlatformProtocol ();
  if (Protocol != NULL) {
    // Protocol function at offset 0x08 = assert handler
    ((VOID (EFIAPI *)(CONST CHAR8 *, UINTN, CONST CHAR8 *))
     (*((VOID ***)Protocol))[1]) (
       FileName,
       LineNumber,
       AssertString
       );
  }
}


/**
 * @brief RTC CMOS register 0x4B read (inline with platform debug).
 *
 * Reads port 0x70 to get current RTC index, preserves the NMI mask
 * (bit 7), selects register 0x4B, then reads platform type from 0x71.
 *
 * Port 0x70 = CMOS index port (NMI mask in bit 7)
 * Port 0x71 = CMOS data port
 *
 * RTC CMOS register 0x4B is an OEM-configurable byte used to encode
 * the platform type identifier (NeonCity FPGA vs other).
 * Values:
 *   0 -> Fallback to hardware strap at 0xFDAF0490
 *   1 -> Debug via route 0x80000004
 *   2,3 -> Debug via route 0x80000006
 *   >3 -> Unchanged, already valid
 */