/** @file
  IioCfgUpdateDxeLightningRidgeEXECB4.c

  IIO Configuration Update DXE driver for the LightningRidge EX EC B4 platform.

  This DXE driver registers platform-specific IIO (Integrated I/O) configuration
  data with the UBA (Universal BIOS Architecture) framework for the LightningRidge
  EX EC B4 platform.

  The driver flow:
    1. Initialize gImageHandle, gST, gBS, gRT global pointers (standard DXE entry)
    2. Locate the IIO configuration HOB via the HOB list in SystemTable
    3. Print debug banner "UBA:IioCfgUpdate-TypeLightningRidgeEXECB4"
    4. Locate the UBA IIO configuration protocol
    5. Dispatch the IIO config platform data header (48 bytes) to four
       GUID-registered protocol instances (one per IIO stack)

  The IIO configuration data header uses signature "PIIO" and describes:
    - Bifurcation mapping (60 entries) starting at offset 0x3C from header
    - PCIe slot configuration count: 1292
    - SMBUS configuration count: 3120
    - Reserved value: 252

  Copyright (C) 2024-2026, Insyde Software Corp. All rights reserved.
  SPDX-License-Identifier: BSD-2-Clause-Patent
**/

#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/DxeHobLib.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/IoLib.h>
#include "IioCfgUpdateDxeLightningRidgeEXECB4.h"

//
// ---------------------------------------------------------------------------
// Global data: IIO configuration platform data header
// ---------------------------------------------------------------------------
//
// This 48-byte structure is the Platform IIO config descriptor dispatched to
// each UBA protocol instance. The bifurcation mapping table follows at the
// byte offset indicated by BifurMapEntryCount acting as the offset (0x3C).
//
STATIC CONST IIO_CFG_PLATFORM_DATA_HEADER  mIioCfgPlatformData = {
  .Signature          = SIGNATURE_32 ('P', 'I', 'I', 'O'),  // 0x4F494950
  .Revision           = 1,
  .TotalDataSize      = 0xD60,      // 3424 bytes total config
  .Reserved0          = 0,
  .BifurMapEntryCount = 60,         // 60 bifurcation entries (offset 0x3C)
  .Reserved1          = 0,
  .SlotConfigCount    = 0x50C,      // 1292 slot config entries
  .Reserved2          = 0,
  .SmbusConfigCount   = 0xC30,      // 3120 SMBUS config entries
  .Reserved3          = 0,
  .Reserved4          = 0xFC,       // 252 reserved
  .Reserved5          = 0
};

//
// ---------------------------------------------------------------------------
// Static variables
// ---------------------------------------------------------------------------

STATIC VOID   *mIioCfgProtocol  = NULL;   ///< Cached UBA IIO config protocol
STATIC VOID   *mIioCfgHobList   = NULL;   ///< Cached IIO config HOB list

//
// ---------------------------------------------------------------------------
// GUID definitions used by this driver
// ---------------------------------------------------------------------------

STATIC CONST EFI_GUID  mUbaIioCfgProtocolGuid    = UBA_IIO_CFG_PROTOCOL_GUID;
STATIC CONST EFI_GUID  mUbaIioCfgUpdateGuid       = UBA_IIO_CFG_UPDATE_PROTOCOL_GUID;
STATIC CONST EFI_GUID  mUbaIioCfgProtocolGuid2    = UBA_IIO_CFG_PROTOCOL_GUID_2;
STATIC CONST EFI_GUID  mUbaIioCfgProtocolGuid3    = UBA_IIO_CFG_PROTOCOL_GUID_3;
STATIC CONST EFI_GUID  mUbaIioCfgProtocolGuid4    = UBA_IIO_CFG_PROTOCOL_GUID_4;

//
// The HOB list is identified by gEfiHobListGuid, but the specific HOB
// entry this driver looks for matches the GUID at 0xC18.
//
STATIC CONST EFI_GUID  mIioCfgHobTargetGuid       = IIO_CFG_HOB_GUID;
STATIC CONST EFI_GUID  mEfiHobListGuid             = HOB_LIST_GUID;

//
// ---------------------------------------------------------------------------
// Debug protocol lookup (cached)
// ---------------------------------------------------------------------------

/**
  Locate and cache the UBA IIO configuration protocol.

  Attempts to locate the protocol identified by mUbaIioCfgProtocolGuid
  (GUID: 6FE6C559-4F35-4111-98E1-332A251512F3). The result is cached
  in mIioCfgProtocol for subsequent calls.

  @return Pointer to the UBA IIO config protocol interface, or NULL on failure.
**/
STATIC
VOID *
GetUbaConfigProtocol (
  VOID
  )
{
  EFI_STATUS  Status;

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

  Status = gBS->LocateProtocol (
                  (EFI_GUID *)&mUbaIioCfgProtocolGuid,
                  NULL,
                  &mIioCfgProtocol
                  );
  if (EFI_ERROR (Status)) {
    mIioCfgProtocol = NULL;
  }

  return mIioCfgProtocol;
}

//
// ---------------------------------------------------------------------------
// CMOS debug output
// ---------------------------------------------------------------------------

/**
  Debug print function utilizing CMOS ports 0x70/0x71 for platform debug
  output routing.

  The debug level is determined by:
    1. Reading CMOS index 0x4B (port 0x70, 0x71)
    2. If value > 3 and value != 0, it is used as-is
    3. If value == 0, reading MMIO register 0xFEDAF0490 bit 1
    4. The effective debug level is (CmosValue - 1)

  @param[in]  ErrorLevel  Debug error level (e.g., 0x80000000 for error).
  @param[in]  Format      Format string.
  @param[in]  ...         Variable argument list.
**/
STATIC
VOID
EFIAPI
DebugLogPrint (
  IN UINTN        ErrorLevel,
  IN CONST CHAR8  *Format,
  ...
  )
{
  UINT8   CmosValue;
  UINT8   DebugLevel;
  VOID    *UbaProtocol;
  VA_LIST VaList;

  VA_START (VaList, Format);

  //
  // Access CMOS debug routing register
  //
  IoWrite8 (0x70, (IoRead8 (0x70) & 0x80) | 0x4B);
  CmosValue = IoRead8 (0x71);

  if (CmosValue > 3) {
    //
    // If value == 0, check MMIO-based debug enable
    //
    if (CmosValue == 0) {
      CmosValue = (MmioRead8 (0xFEDAF0490) & 2) | 1;
    }
  }

  DebugLevel = CmosValue - 1;
  if (DebugLevel <= 0xFD) {
    DebugLevel = 4;
  }

  //
  // Route to UBA protocol debug output if available
  //
  UbaProtocol = GetUbaConfigProtocol ();
  if (UbaProtocol != NULL) {
    //
    // Protocol interface at offset +8 is the DebugPrint function
    //
    ((EFI_STATUS (EFIAPI *)(VOID *, UINTN, CONST CHAR8 *, VA_LIST))
      ((UINT64 *)UbaProtocol)[1]) (UbaProtocol, ErrorLevel, Format, VaList);
  }

  VA_END (VaList);
}

//
// ---------------------------------------------------------------------------
// ASSERT handler
// ---------------------------------------------------------------------------

/**
  Assertion failure handler. Displays the failed assertion via the UBA
  debug protocol.

  @param[in]  FileName    Source file name where the assertion occurred.
  @param[in]  LineNumber  Line number of the assertion.
  @param[in]  Description Assertion expression text.
**/
STATIC
VOID
EFIAPI
DebugAssertHandler (
  IN CONST CHAR8  *FileName,
  IN UINTN        LineNumber,
  IN CONST CHAR8  *Description
  )
{
  VOID  *UbaProtocol;

  UbaProtocol = GetUbaConfigProtocol ();
  if (UbaProtocol != NULL) {
    //
    // Protocol interface at offset +8 is the DebugPrint function,
    // but here we call it in ASSERT mode (no VA_LIST needed)
    //
    ((EFI_STATUS (EFIAPI *)(CONST CHAR8 *, UINTN, CONST CHAR8 *))
      ((UINT64 *)UbaProtocol)[1]) (FileName, LineNumber, Description);
  }
}

//
// ---------------------------------------------------------------------------
// Unaligned memory access
// ---------------------------------------------------------------------------

/**
  Reads a 64-bit value from an unaligned memory address.

  ASSERTs if Buffer is NULL.

  @param[in]  Buffer  Pointer to the unaligned 64-bit value.

  @return The 64-bit value at Buffer.
**/
STATIC
UINT64
ReadUnaligned64 (
  IN CONST VOID  *Buffer
  )
{
  ASSERT (Buffer != NULL);
  return *(UINT64 *)Buffer;
}

//
// ---------------------------------------------------------------------------
// GUID comparison (as two UINT64 values)
// ---------------------------------------------------------------------------

/**
  Compares two GUIDs by treating them as two UINT64 values each.

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

  @return TRUE if the GUIDs match, FALSE otherwise.
**/
STATIC
BOOLEAN
CompareGuid (
  IN EFI_GUID  *Guid1,
  IN EFI_GUID  *Guid2
  )
{
  if (ReadUnaligned64 (Guid1) == ReadUnaligned64 (Guid2) &&
      ReadUnaligned64 ((UINT8 *)Guid1 + 8) == ReadUnaligned64 ((UINT8 *)Guid2 + 8)) {
    return TRUE;
  }
  return FALSE;
}

//
// ---------------------------------------------------------------------------
// HOB list retrieval
// ---------------------------------------------------------------------------

/**
  Retrieves the IIO configuration HOB from the UEFI System Table HOB list.

  Walks the HOB array (SystemTable + 0x68 holds count, + 0x70 holds pointer).
  Each HOB entry is 24 bytes:
    [0:8]   GUID first QWORD
    [8:16]  GUID second QWORD
    [16:24] Data pointer

  Searches for a HOB whose GUID matches mIioCfgHobTargetGuid.

  @return Pointer to the IIO config HOB data, or NULL if not found.
**/
STATIC
VOID *
GetIioConfigHobList (
  VOID
  )
{
  UINTN   HobCount;
  UINTN   Index;
  VOID    *HobArray;
  UINT64  TargetGuid0;
  UINT64  TargetGuid1;

  //
  // Return cached result
  //
  if (mIioCfgHobList != NULL) {
    return mIioCfgHobList;
  }

  mIioCfgHobList = NULL;

  //
  // SystemTable HOB fields:
  //   +0x68 = Number of HOBs
  //   +0x70 = Pointer to HOB array
  //
  HobCount = *(UINTN *)((UINT8 *)gST + 0x68);
  if (HobCount != 0) {
    HobArray = *(VOID **)((UINT8 *)gST + 0x70);

    TargetGuid0 = ReadUnaligned64 (&mIioCfgHobTargetGuid);
    TargetGuid1 = ReadUnaligned64 ((UINT8 *)&mIioCfgHobTargetGuid + 8);

    //
    // Linear scan through HOB array (each entry 24 bytes)
    //
    for (Index = 0; Index < HobCount; Index++) {
      //
      // Compare GUID at HOB entry offset 0
      //
      if (ReadUnaligned64 ((UINT8 *)HobArray + Index * 24) == TargetGuid0 &&
          ReadUnaligned64 ((UINT8 *)HobArray + Index * 24 + 8) == TargetGuid1) {
        //
        // Found -- data pointer at HOB entry offset 0x10
        //
        mIioCfgHobList = *(VOID **)((UINT8 *)HobArray + Index * 24 + 0x10);
        return mIioCfgHobList;
      }
    }
  }

  //
  // HOB not found -- trigger assertion
  //
  DebugLogPrint (0x80000000, L"\nASSERT_EFI_ERROR (Status = %r)\n");
  DebugAssertHandler (
    "e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
    0x36,
    "!EFI_ERROR (Status)"
    );

  if (mIioCfgHobList == NULL) {
    DebugAssertHandler (
      "e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
      0x37,
      "mHobList != ((void *) 0)"
      );
  }

  return mIioCfgHobList;
}

//
// ---------------------------------------------------------------------------
// IIO configuration platform data registration
// ---------------------------------------------------------------------------

/**
  Register the IIO configuration platform data with UBA protocol instances.

  Locates the UBA IIO configuration protocol (GUID E03E0D46-5263-4845-B0A4-58D57B3177E2),
  then calls the protocol's SetData function (at offset +0x10) for each of the
  four IIO stack GUIDs:
    1. UBA_IIO_CFG_PROTOCOL_GUID       (6FE6C559-...)
    2. UBA_IIO_CFG_UPDATE_PROTOCOL_GUID (0F722F2A-...)
    3. UBA_IIO_CFG_PROTOCOL_GUID_3     (EBD11A00-...)
    4. UBA_IIO_CFG_PROTOCOL_GUID_4     (82D03B12-...)

  Each call passes the same 48-byte IIO_CFG_PLATFORM_DATA_HEADER, which the
  protocol uses to register per-stack bifurcation and slot config data.

  @return EFI_SUCCESS if all registrations succeed, or the first error status.
**/
EFI_STATUS
RegisterIioConfigPlatformData (
  VOID
  )
{
  EFI_STATUS  Status;
  VOID        *UbaProtocol;

  //
  // Locate the UBA IIO configuration protocol
  //
  Status = gBS->LocateProtocol (
                  (EFI_GUID *)&mUbaIioCfgProtocolGuid2,  // E03E0D46-...
                  NULL,
                  &UbaProtocol
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Register config data for each IIO stack GUID
  // Protocol function at offset +0x10 = SetPlatformData(This, RegisterGuid, DataBuffer, DataSize)
  //
  Status = ((EFI_STATUS (EFIAPI *)(
              VOID *,           // This
              EFI_GUID *,       // Register GUID
              VOID *,           // Data buffer
              UINTN             // Data size
              ))((UINT64 *)UbaProtocol)[2])(
              UbaProtocol,
              (EFI_GUID *)&mUbaIioCfgProtocolGuid,   // 6FE6C559-...
              (VOID *)&mIioCfgPlatformData,
              sizeof (mIioCfgPlatformData)             // 48 bytes
              );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Second stack registration
  //
  Status = ((EFI_STATUS (EFIAPI *)(
              VOID *,
              EFI_GUID *,
              VOID *,
              UINTN
              ))((UINT64 *)UbaProtocol)[2])(
              UbaProtocol,
              (EFI_GUID *)&mUbaIioCfgUpdateGuid,     // 0F722F2A-...
              (VOID *)&mIioCfgPlatformData,
              sizeof (mIioCfgPlatformData)
              );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Third stack registration
  //
  Status = ((EFI_STATUS (EFIAPI *)(
              VOID *,
              EFI_GUID *,
              VOID *,
              UINTN
              ))((UINT64 *)UbaProtocol)[2])(
              UbaProtocol,
              (EFI_GUID *)&mUbaIioCfgProtocolGuid3,  // EBD11A00-...
              (VOID *)&mIioCfgPlatformData,
              sizeof (mIioCfgPlatformData)
              );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Fourth stack registration
  //
  Status = ((EFI_STATUS (EFIAPI *)(
              VOID *,
              EFI_GUID *,
              VOID *,
              UINTN
              ))((UINT64 *)UbaProtocol)[2])(
              UbaProtocol,
              (EFI_GUID *)&mUbaIioCfgProtocolGuid4,  // 82D03B12-...
              (VOID *)&mIioCfgPlatformData,
              sizeof (mIioCfgPlatformData)
              );

  return Status;
}

//
// ---------------------------------------------------------------------------
// Driver entry point
// ---------------------------------------------------------------------------

/**
  Main entry point for the IioCfgUpdateDxeLightningRidgeEXECB4 driver.

  Initializes the UEFI global service pointers (gImageHandle, gST, gBS, gRT),
  retrieves the IIO configuration HOB, prints a debug banner, then dispatches
  the IIO configuration platform data to the UBA protocol.

  @param[in]  ImageHandle  The firmware allocated handle for the EFI image.
  @param[in]  SystemTable  A pointer to the EFI System Table.

  @retval EFI_SUCCESS           Driver initialized and IIO config registered.
  @retval EFI_UNSUPPORTED       UBA protocol not found.
  @retval other                 Error from RegisterIioConfigPlatformData.
**/
EFI_STATUS
EFIAPI
_ModuleEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  //
  // Initialize global UEFI service pointer cache
  //
  gImageHandle = ImageHandle;
  ASSERT (ImageHandle != NULL);

  gST = SystemTable;
  ASSERT (SystemTable != NULL);

  gBS = SystemTable->BootServices;
  ASSERT (gBS != NULL);

  gRT = SystemTable->RuntimeServices;
  ASSERT (gRT != NULL);

  //
  // Retrieve the IIO configuration HOB list (locates HOBs in SystemTable)
  //
  GetIioConfigHobList ();

  //
  // Print platform identification string
  //
  DebugLogPrint (0x80000000, "UBA:IioCfgUpdate-TypeLightningRidgeEXECB4\n");

  //
  // Register IIO configuration platform data
  //
  return RegisterIioConfigPlatformData ();
}