/** @file
  FpgaConfigDataDxeNeonCityFPGA -- UEFI DXE driver that provides FPGA
  configuration data access for the NeonCityFPGA platform.

  This driver registers a UBA protocol that exposes a callback for querying
  HSSI (High Speed Serial Interface) card-specific FPGA configuration data
  (PXB register offsets and configuration pointers). The protocol GUID is
  platform-specific: 332B3586-F0B4-46EE-A39C-786330E2D328.

  Build path:
    e:\hs\PurleyRpPkg\Uba\UbaMain\Dxe\TypeNeonCityFPGA\FpgaConfigDataDxe\

  Toolchain: VS2015, X64 DEBUG build
  Image size: 0x6B00 bytes  (.text: 0xA60, .rdata: 0x740, .data: 0x5540)
**/

#ifndef FPGA_CONFIG_DATA_DXE_NEON_CITY_FPGA_H
#define FPGA_CONFIG_DATA_DXE_NEON_CITY_FPGA_H

#include "../uefi_headers/Uefi.h"

// ---------------------------------------------------------------------------
// GUID Definitions
// ---------------------------------------------------------------------------

///
/// Protocol GUID exposed by this driver. Other UBA drivers use this GUID to
/// locate the GetFpgaConfigData function via gBS->LocateProtocol().
///
#define FPGA_CONFIG_PROTOCOL_GUID \
  { 0x332B3586, 0xF0B4, 0x46EE, { 0xA3, 0x9C, 0x78, 0x63, 0x30, 0xE2, 0xD3, 0x28 } }

extern EFI_GUID gFpgaConfigProtocolGuid;

///
/// EFI_HOB_LIST_GUID (standard UEFI) -- used to locate the HOB list from
/// the system configuration table.
///
#define EFI_HOB_LIST_GUID \
  { 0x7739F24C, 0x93D7, 0x11D4, { 0x9A, 0x3A, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D } }

extern EFI_GUID gEfiHobListGuid;

///
/// EFI_GENERIC_VARIABLE_GUID -- used to read/write the "FpgaSocketConfig"
/// UEFI variable for persisting FPGA socket configuration across boots.
///
#define EFI_GENERIC_VARIABLE_GUID \
  { 0x59D7C710, 0x49EA, 0x4D26, { 0x9F, 0x7C, 0xDE, 0x10, 0x64, 0x87, 0x6E, 0x2F } }

extern EFI_GUID gEfiGenericVariableGuid;

///
/// Second GUID at 0x1470 - unknown purpose, used in HOB GUID comparison
/// for gEfiHobListGuid. Acts as a sentinel or dual-GUID matching pattern.
///
#define HOB_SENTINEL_GUID \
  { 0x36232936, 0x0E76, 0x31C8, { 0xA1, 0x3A, 0x3A, 0xF2, 0xFC, 0x1C, 0x39, 0x32 } }

extern EFI_GUID gHobSentinelGuid;

///
/// Third GUID at 0x1480 -- used as vendor GUID for the "FpgaSocketConfig"
/// UEFI variable when calling gRT->SetVariable().
///
#define FPGA_SOCKET_CONFIG_VENDOR_GUID \
  { 0x75839B0B, 0x0A99, 0x4233, { 0x8A, 0xA4, 0x38, 0x66, 0xF6, 0xCE, 0xF4, 0xB3 } }

extern EFI_GUID gFpgaSocketConfigVendorGuid;

// ---------------------------------------------------------------------------
// HSSI Card Type Definitions
// ---------------------------------------------------------------------------

///
/// HSSI card type values stored in the HOB at offset mFpgaConfigData.HssiCardId[i]
///
#define HSSI_CARD_TYPE_NONE         0x00  ///< No HSSI card present
#define HSSI_CARD_TYPE_UNKNOWN      0x07  ///< Reserved/reserved type
#define HSSI_CARD_TYPE_1            0x04  ///< Type 1 (e.g., 2-port 10G/25G)
#define HSSI_CARD_TYPE_2            0x06  ///< Type 2 (e.g., 4-port 10G/25G)

///
/// Maximum number of sockets supported
///
#define MAX_SOCKET_COUNT            4

///
/// Maximum number of HSSI card config table entries
///
#define MAX_HSSI_CONFIG_ENTRIES     2

// ---------------------------------------------------------------------------
// HOB Data Structure
// ---------------------------------------------------------------------------

///
/// Size of the FPGA configuration HOB (Hand-Off Block).
/// This HOB is identified by checking the first two GUIDs in sequence
/// (gFpgaConfigProtocolGuid followed by the sentinel), then extracting
/// the payload that starts at offset +0x18 within the matched HOB block.
///
#define FPGA_CONFIG_HOB_SIZE        38

#pragma pack(push, 1)

///
/// FPGA configuration data structure stored in a UEFI HOB.
/// This is the per-socket FPGA and HSSI card configuration.
///
typedef struct {
  ///
  /// +0x00 (byte_6960[0]): Initialized flag. Set to 1 when configuration is
  /// fully loaded from HOB or variable.
  ///
  UINT8     Initialized;

  ///
  /// +0x01 (byte_6961): Reserved / version field.
  ///
  UINT8     Reserved1;

  ///
  /// +0x02 (byte_6962): FpgaSktIoApicId -- IO APIC ID for the FPGA socket.
  ///
  UINT8     FpgaSktIoApicId;

  ///
  /// +0x03 (byte_6963): FpgaSktActive -- bitmask of active FPGA sockets.
  ///   Bit 0 = Socket 0 active
  ///   Bit 1 = Socket 1 active
  ///   ...
  ///
  UINT8     FpgaSktActive;

  ///
  /// +0x04 (byte_6964): SktIioApicId0 -- IIO APIC ID 0.
  ///
  UINT8     SktIioApicId0;

  ///
  /// +0x05 (byte_6965): ActiveIioStackMask -- bitmask of active IIO stacks.
  ///
  UINT8     ActiveIioStackMask;

  ///
  /// +0x06 (byte_6966): CpuNum -- number of CPUs in the socket.
  ///
  UINT8     CpuNum;

  ///
  /// +0x07-0x0A: Padding.
  ///
  UINT8     Reserved2[4];

  ///
  /// +0x0B (byte_6960[11]): HssiCardId[0] -- HSSI card type for socket 0.
  ///
  UINT8     HssiCardId_0;

  ///
  /// +0x0C (byte_6960[12]): Reserved/hssi card sub-ID.
  ///
  UINT8     HssiCardSubId_0;

  ///
  /// +0x0D (byte_6960[13]): Unused/padding.
  ///
  UINT8     Padding1;

  ///
  /// +0x0E (byte_6960[14]): HssiBifurcation[0] -- PCIe bifurcation setting
  /// for HSSI card on socket 0.
  ///
  UINT8     HssiBifurcation_0;

  ///
  /// +0x0F (byte_6967): HssiCardId[1] -- HSSI card type for socket 1.
  ///
  UINT8     HssiCardId_1;

  ///
  /// +0x10 (byte_6967+1): HssiCardSubId[1].
  ///
  UINT8     HssiCardSubId_1;

  ///
  /// +0x11 (byte_6967+2): Unused.
  ///
  UINT8     Padding2;

  ///
  /// +0x12 (byte_6967+3): HssiBifurcation[1].
  ///
  UINT8     HssiBifurcation_1;

  ///
  /// +0x13 (byte_6960[19]): HssiCardId[2].
  ///
  UINT8     HssiCardId_2;

  ///
  /// +0x14 (byte_6960[20]): HssiCardSubId[2].
  ///
  UINT8     HssiCardSubId_2;

  ///
  /// +0x15 (byte_6960[21]): Unused.
  ///
  UINT8     Padding3;

  ///
  /// +0x16 (byte_6960[22]): HssiBifurcation[2].
  ///
  UINT8     HssiBifurcation_2;

  ///
  /// +0x17 (byte_6960[23]): HssiCardId[3].
  ///
  UINT8     HssiCardId_3;

  ///
  /// +0x18 (byte_6960[24]): HssiCardSubId[3].
  ///
  UINT8     HssiCardSubId_3;

  ///
  /// +0x19 (byte_6960[25]): Unused.
  ///
  UINT8     Padding4;

  ///
  /// +0x1A (byte_6960[26]): HssiBifurcation[3].
  ///
  UINT8     HssiBifurcation_3;

  ///
  /// +0x1B-0x1E: Padding / reserved (byte_6960[27-30]).
  ///
  UINT8     Reserved3[4];

  ///
  /// +0x1F (byte_697F): FpgaSktIoApicId2 -- alternate/secondary IO APIC ID.
  ///
  UINT8     FpgaSktIoApicId2;

  ///
  /// +0x20 (byte_6980): IioApicId0 -- APIC ID 0 for IIO.
  ///
  UINT8     IioApicId0;

  ///
  /// +0x21-0x24: Reserved.
  ///
  UINT8     Reserved4[4];

  ///
  /// +0x25 (byte_6985): IioApicIdOverride -- override value for IIO APIC ID.
  ///
  UINT8     IioApicIdOverride;
} FPGA_CONFIG_DATA;

#pragma pack(pop)

// ---------------------------------------------------------------------------
// HSSI Card Configuration Table
// ---------------------------------------------------------------------------

///
/// Per-socket HSSI card configuration entry.
/// Each entry maps a (socket, card_type) pair to platform-specific data.
/// There are up to MAX_HSSI_CONFIG_ENTRIES entries at 0x3ED0 in .data.
///
#pragma pack(push, 1)

typedef struct {
  ///
  /// +0x00: Socket index (0, 1, ...)
  ///
  UINT8     Socket;

  ///
  /// +0x01: HSSI card type (see HSSI_CARD_TYPE_* defines)
  ///
  UINT8     CardType;

  ///
  /// +0x02-0x07: Padding.
  ///
  UINT8     Reserved[6];

  ///
  /// +0x08: Pointer to PXB (PCI Express Root Port) configuration data.
  ///
  VOID      *PxbConfig;

  ///
  /// +0x10: Pointer to the GetFpgaConfigData callback function (sub_51C
  ///        wrapper or actual config access function at offset 0x448).
  ///
  VOID      *ConfigDataPtr;
} HSSI_CARD_CONFIG_ENTRY;

#pragma pack(pop)

///
/// HSSI card configuration table -- array of MAX_HSSI_CONFIG_ENTRIES entries.
///
extern HSSI_CARD_CONFIG_ENTRY  mHssiCardConfigTable[MAX_HSSI_CONFIG_ENTRIES];

// ---------------------------------------------------------------------------
// Debug Error Level Constants
// ---------------------------------------------------------------------------

///
/// Error level masks used in debug output filtering.
/// These are the DP_* constants defined in MdePkg for the DebugLib protocol.
///
#define DEBUG_ERROR               0x80000000  ///< Error messages
#define DEBUG_INFO                0x80000040  ///< Informational messages

// ---------------------------------------------------------------------------
// Protocol Interface
// ---------------------------------------------------------------------------

///
/// FPGA Configuration Protocol interface.
/// This protocol is installed at 0x1460 with GUID 332B3586-F0B4-46EE-A39C-786330E2D328.
/// The single function in the interface is GetFpgaConfigData.
///
typedef struct {
  ///
  /// Get FPGA configuration data for a given socket and HSSI card type.
  ///
  /// @param[in]  Socket         Socket index (0-based).
  /// @param[in]  HssiCardType   HSSI card type identifier.
  /// @param[out] PxbConfig      Pointer to receive the PXB configuration data pointer.
  /// @param[out] ConfigDataPtr  Pointer to receive the config data callback pointer.
  ///
  /// @retval EFI_SUCCESS             Data returned successfully.
  /// @retval EFI_INVALID_PARAMETER   PxbConfig or ConfigDataPtr is NULL.
  /// @retval EFI_NOT_FOUND           No entry matches the given Socket/HssiCardType.
  /// @retval EFI_UNSUPPORTED         The HSSI card type is not supported.
  ///
  EFI_STATUS
  (EFIAPI *GetFpgaConfigData)(
    IN  UINT8   Socket,
    IN  UINT8   HssiCardType,
    OUT VOID    **PxbConfig,
    OUT VOID    **ConfigDataPtr
    );
} FPGA_CONFIG_PROTOCOL;

// ---------------------------------------------------------------------------
// Global Variables (from .data / .bss)
// ---------------------------------------------------------------------------

extern EFI_SYSTEM_TABLE       *gST;             ///< 0x6920
extern EFI_BOOT_SERVICES      *gBS;             ///< 0x6928
extern EFI_HANDLE             gImageHandle;     ///< 0x6930
extern EFI_RUNTIME_SERVICES   *gRT;             ///< 0x6938
extern VOID                   *mDebugProtocol;  ///< 0x6940 - cached DebugLib protocol
extern VOID                   *mHobList;        ///< 0x6948 - cached HOB list pointer
extern UINT8                  mCmosDebugLevel;  ///< 0x6950 - cached CMOS debug level

///
/// FPGA configuration data array, one entry per socket (4 sockets).
/// Populated from HOB or UEFI variable "FpgaSocketConfig".
///
extern FPGA_CONFIG_DATA       mFpgaConfigData;  ///< 0x6960

// ---------------------------------------------------------------------------
// Function Declarations
// ---------------------------------------------------------------------------

/**
  UEFI module entry point. Called by the DXE dispatcher.

  Initializes gImageHandle, gST, gBS, gRT with standard UefiBootServicesTableLib
  assertions. Then calls sub_870 to locate the HOB list, and sub_51C to perform
  the FPGA configuration data setup.

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

  @return EFI_STATUS from sub_51C.
**/
EFI_STATUS
EFIAPI
_ModuleEntryPoint(
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  );

/**
  Queries FPGA configuration data for a given socket and HSSI card type.

  Looks up the (Socket, CardType) pair in mHssiCardConfigTable. If found,
  returns the PXB configuration data pointer and the config data callback pointer.

  @param[in]  Socket         Socket index (0-based).
  @param[in]  HssiCardType   HSSI card type (see HSSI_CARD_TYPE_*).
  @param[out] PxbConfig      Receives PXB config data pointer.
  @param[out] ConfigDataPtr  Receives config data pointer.

  @retval EFI_SUCCESS             Lookup succeeded.
  @retval EFI_INVALID_PARAMETER   PxbConfig or ConfigDataPtr is NULL.
  @retval EFI_NOT_FOUND           No matching table entry.
  @retval EFI_UNSUPPORTED         Unsupported HSSI card type (value 3, 5, etc.).
**/
EFI_STATUS
EFIAPI
GetFpgaConfigData(
  IN  UINT8   Socket,
  IN  UINT8   HssiCardType,
  OUT VOID    **PxbConfig,
  OUT VOID    **ConfigDataPtr
  );

/**
  Main FPGA configuration data entry function.

  Extracts configuration from the FPGA_CONFIG_DATA HOB (located by sub_9A0),
  populates mFpgaConfigData, and installs the FPGA_CONFIG_PROTOCOL.

  @param[in] ImageHandle  Image handle for protocol installation.
  @param[in] Buffer       Pointer to the HOB data buffer from sub_9A0.

  @return EFI_SUCCESS on success, error code otherwise.
**/
EFI_STATUS
EFIAPI
FpgaConfigDataEntry(
  IN EFI_HANDLE  ImageHandle,
  IN VOID        *Buffer
  );

/**
  Initializes FPGA configuration data from HOB or UEFI variable.

  Locates the FPGA configuration HOB by scanning the HOB list for the
  paired GUID sequence (gFpgaConfigProtocolGuid + gHobSentinelGuid).
  If not found in HOB, creates a new HOB and initializes it from the
  "FpgaSocketConfig" UEFI variable.

  @param[out] ConfigData  Receives pointer to the FPGA configuration data.

  @retval EFI_SUCCESS             HOB located or created.
  @retval EFI_NOT_FOUND           HOB not found and creation failed.
**/
EFI_STATUS
EFIAPI
InitializeFpgaConfigData(
  OUT VOID  **ConfigData
  );

// ---------------------------------------------------------------------------
// Internal Helper Functions
// ---------------------------------------------------------------------------

/**
  Locates the HOB list from the system configuration table.

  Scans SystemTable->ConfigurationTable[] for EFI_HOB_LIST_GUID
  (7739F24C-93D7-11D4-9A3A-0090273FC14D). Caches result in mHobList.

  @return Pointer to the HOB list, or NULL if not found.
**/
VOID *
EFIAPI
GetHobList(
  VOID
  );

/**
  Scans the HOB list for a FPGA configuration HOB.

  Walks the HOB list starting from mHobList, looking for a HOB whose
  GUID matches the paired sequence (gFpgaConfigProtocolGuid +
  gHobSentinelGuid). If found, returns a pointer to the HOB payload.

  If no suitable HOB is found, creates a new FPGA configuration HOB
  and initializes it from the "FpgaSocketConfig" UEFI variable (if
  available) or with default zero values.

  @param[out] Buffer  Receives pointer to FPGA configuration data.

  @retval EFI_SUCCESS  FPGA config data pointer returned successfully.
**/
EFI_STATUS
EFIAPI
GetFpgaConfigHob(
  OUT VOID  **Buffer
  );

/**
  Determines the debug output level by reading CMOS register 0x4B.

  Reads CMOS register 0x4B via RTC ports 0x70/0x71. If the register
  value is 0, falls back to reading MMIO at 0xFDAF0490 (bit 1).

  @return Debug level: 1 (normal), 2, 3, or 4 (verbose).
**/
UINT8
EFIAPI
GetCmosDebugLevel(
  VOID
  );

/**
  Debug print function with level filtering.

  Compares the ErrorLevel parameter against the CMOS debug level.
  Only prints if the level is permitted.

  @param[in] ErrorLevel  DEBUG_* error level mask.
  @param[in] Format      Print format string.
  @param[in] ...         Additional format arguments.
**/
VOID
EFIAPI
DebugPrint(
  IN UINTN       ErrorLevel,
  IN CONST CHAR8 *Format,
  ...
  );

/**
  ASSERT handler -- calls DebugLib protocol assertion.

  @param[in] FileName  Source file name string.
  @param[in] LineNumber  Line number of the assertion.
  @param[in] Description  Assertion description string.
**/
VOID
EFIAPI
DebugAssert(
  IN CONST CHAR8  *FileName,
  IN UINTN        LineNumber,
  IN CONST CHAR8  *Description
  );

/**
  Lazily resolves and caches the DebugLib protocol.

  Uses gBS->LocateProtocol() to find the DebugLib protocol interface.
  Caches in mDebugProtocol.

  @return Pointer to DebugLib protocol interface, or NULL if not found.
**/
VOID *
EFIAPI
GetDebugProtocol(
  VOID
  );

/**
  Compares two GUIDs by comparing their two 8-byte halves.

  @param[in] Guid1  Pointer to first GUID.
  @param[in] Guid2  Pointer to second GUID.

  @return TRUE if the GUIDs match.
**/
BOOLEAN
EFIAPI
CompareGuid(
  IN EFI_GUID  *Guid1,
  IN EFI_GUID  *Guid2
  );

/**
  Reads a UINT64 from an unaligned pointer with NULL check.

  @param[in] Buffer  Pointer to the UINT64 value.

  @return The UINT64 value at Buffer.
**/
UINT64
EFIAPI
ReadUnaligned64(
  IN CONST UINT64  *Buffer
  );

/**
  Allocates pool memory of the given type and size.

  Calls gBS->AllocatePool().

  @param[in]  PoolType  Type of pool to allocate.
  @param[in]  Size      Number of bytes to allocate.
  @param[out] Buffer    Receives the allocated buffer pointer.

  @return EFI_STATUS from gBS->AllocatePool().
**/
VOID *
EFIAPI
AllocatePool(
  IN EFI_MEMORY_TYPE  PoolType,
  IN UINTN            Size
  );

/**
  Frees pool memory previously allocated by AllocatePool.

  Calls gBS->FreePool().

  @param[in] Buffer  Pointer to memory to free.
**/
VOID
EFIAPI
FreePool(
  IN VOID  *Buffer
  );

/**
  Zeroes a memory buffer.

  @param[in] Buffer  Pointer to memory to zero.
  @param[in] Size    Number of bytes to zero.

  @return Buffer.
**/
VOID *
EFIAPI
ZeroMem(
  IN VOID   *Buffer,
  IN UINTN  Size
  );

#endif // FPGA_CONFIG_DATA_DXE_NEON_CITY_FPGA_H