/** @file
  SlotDataUpdateDxeLightningRidgeEXECB4 - PCIe Slot Data Configuration

  This UEFI DXE driver provides slot data configuration for the LightningRidge EX
  EC B4 platform. It publishes platform-specific slot table protocol interfaces
  to the DXE protocol database during the entry point.

  The driver performs the following:
    - Initializes UEFI boot/runtime services table pointers
    - Retrieves the HOB list from the system table
    - Prints debug trace via UBA debug protocol
    - Locates the platform slot protocol
    - Installs slot configuration protocol interfaces with PCIe slot table data

  Copyright (c) 2025, Intel Corporation. All rights reserved.
  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "SlotDataUpdateDxeLightningRidgeEXECB4.h"

//
// Global variables for UEFI system table pointers
//
EFI_HANDLE             gImageHandle   = NULL;
EFI_SYSTEM_TABLE       *gSystemTable  = NULL;
EFI_BOOT_SERVICES      *gBootServices = NULL;
EFI_RUNTIME_SERVICES   *gRuntimeServices = NULL;

//
// Cached protocol and HOB pointers
//
STATIC UBA_DEBUG_PROTOCOL  *mDebugPrintProtocol   = NULL;
STATIC VOID                *mHobList              = NULL;

//
// Platform slot table data - LightningRidge EX EC B4
// These tables define the PCIe slot configuration for the platform
//
STATIC CONST PLATFORM_SLOT_TABLE mSlotTableConfig1 = {
  SLOT_TABLE_SIGNATURE,     // Signature 'PSLT'
  1,                        // Version
  0xBB0,                    // GpioConfigBase
  0x4B0,                    // SlotConfigBase
  1,                        // SlotCount
  0                         // Reserved
};

STATIC CONST PLATFORM_SLOT_TABLE mSlotTableConfig2 = {
  SLOT_TABLE_SIGNATURE,     // Signature 'PSLT'
  1,                        // Version
  0xBB0,                    // GpioConfigBase
  0x4B0,                    // SlotConfigBase
  1,                        // SlotCount
  0                         // Reserved
};

/**
  UBA debug print wrapper.
  Locates the UBA debug print protocol and uses it to print the debug message.

  @param[in] ErrorLevel  Debug error level.
  @param[in] Format      Format string.
  @param[in] ...         Variable arguments for format string.

  @return Status of the debug print operation.

**/
UINTN
UbaDebugPrint (
  IN UINTN       ErrorLevel,
  IN CONST CHAR8 *Format,
  ...
  )
{
  VA_LIST                 Marker;
  UBA_DEBUG_PROTOCOL      *Protocol;
  UINTN                   Result;

  Protocol = mDebugPrintProtocol;
  Result = 0;

  if (Protocol != NULL) {
    //
    // Read CMOS status register to check debug state
    //
    IoWrite8 (0x70, (IoRead8 (0x70) & 0x80) | 0x4B);
    UINT8 DebugLevel = IoRead8 (0x71);

    if (DebugLevel > 3) {
      //
      // Read debug enable from platform scratchpad (F000 segment)
      //
      if (DebugLevel == 0) {
        DebugLevel = (*(volatile UINT8 *)(UINTN)0xFDAF0490 & 2) | 1;
      }
    }

    if ((DebugLevel - 1) <= 0xFD) {
      //
      // Determine the actual debug print level based on CMOS
      //
      UINTN PrintErrorLevel = 0x80000086; // DEBUG_INFO | DEBUG_LOAD
      if (DebugLevel == 1) {
        PrintErrorLevel = 0x80000004; // DEBUG_ERROR
      }

      if ((PrintErrorLevel & ErrorLevel) != 0) {
        VA_START (Marker, Format);
        Result = Protocol->DebugPrint (ErrorLevel, Format, &Marker);
        VA_END (Marker);
      }
    }
  }

  return Result;
}

/**
  Debug assertion handler.
  Invokes the UBA debug print protocol to report assertion failures.

  @param[in] FileName    Source file name of the assertion.
  @param[in] LineNumber  Line number of the assertion.
  @param[in] Description Assertion description string.

**/
VOID
EFIAPI
AssertHandler (
  IN CONST CHAR8 *FileName,
  IN UINTN       LineNumber,
  IN CONST CHAR8 *Description
  )
{
  if (mDebugPrintProtocol != NULL) {
    mDebugPrintProtocol->DebugPrint ((UINTN)FileName, LineNumber, (UINTN)Description);
  }
}

/**
  Initializes the UBA debug print protocol.

  The protocol is located via the UEFI boot services and cached for
  subsequent use by the debug print and assertion functions.

**/
STATIC
VOID
InitializeDebugProtocol (
  VOID
  )
{
  EFI_STATUS  Status;

  if (mDebugPrintProtocol != NULL) {
    return;
  }

  //
  // Allocate pool for protocol lookup key
  //
  UINTN PoolSize = (*(UINTN (EFIAPI **)(UINTN))(gBootServices + 24))(31);
  (*(VOID (EFIAPI **)(UINTN))(gBootServices + 32))(PoolSize);

  if (PoolSize <= 16) {
    Status = (*(EFI_STATUS (EFIAPI **)(VOID *, VOID *, VOID **))(gBootServices + 320))(
               &gUbaDebugProtocolGuid,
               NULL,
               &mDebugPrintProtocol
               );
    if (EFI_ERROR (Status)) {
      mDebugPrintProtocol = NULL;
    }
  } else {
    mDebugPrintProtocol = NULL;
  }
}

/**
  Reads a 64-bit value from the specified address (unaligned-safe read).

  @param[in] Buffer  Pointer to the buffer to read from.

  @return The 64-bit value at the specified address.

**/
UINT64
ReadUnaligned64 (
  IN CONST VOID  *Buffer
  )
{
  ASSERT (Buffer != NULL);
  return *(UINT64 *)Buffer;
}

/**
  Checks if a HOB entry matches the platform-specific HOB GUID.

  @param[in] ImageHandle  The firmware allocated handle for the EFI image.
  @param[in] HobEntry     Pointer to a HOB entry header.

  @retval TRUE   The HOB entry GUID matches the platform GUID.
  @retval FALSE  The HOB entry GUID does not match.

**/
BOOLEAN
IsPlatformSlotHob (
  IN EFI_HANDLE  ImageHandle,
  IN VOID        *HobEntry
  )
{
  GUID  *HobGuid;
  GUID  PlatformHobGuid = LIGHTNING_RIDGE_EX_EC_B4_PLATFORM_HOB_GUID;

  HobGuid = (GUID *)HobEntry;

  return (CompareMem (&HobGuid->Data1, &PlatformHobGuid.Data1, sizeof (UINT64)) == 0 &&
          CompareMem (&HobGuid->Data4, &PlatformHobGuid.Data4, sizeof (UINT64)) == 0);
}

/**
  Retrieves the HOB list pointer from the system table.

  Iterates through the HOB entries in the system table's HOB list to find
  the gEfiHobListGuid entry and returns a pointer to the HOB list.

  @param[in] ImageHandle  The firmware allocated handle for the EFI image.

  @return Pointer to the HOB list, or NULL if not found.

**/
VOID *
GetHobListInternal (
  IN EFI_HANDLE  ImageHandle
  )
{
  UINTN  HobCount;
  UINTN  Index;

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

  HobCount = gSystemTable->HobList.Raw != NULL ? *((UINTN *)gSystemTable->HobList.Raw) : 0;
  mHobList = NULL;

  if (gSystemTable->HobList.Raw != NULL) {
    for (Index = 0; Index < HobCount; Index++) {
      if (IsPlatformSlotHob (ImageHandle, (VOID *)(*((UINTN *)gSystemTable->HobList.Raw) + Index * 24))) {
        mHobList = (VOID *)(*((UINTN *)gSystemTable->HobList.Raw + 24 * Index + 16));
        break;
      }
    }
  }

  //
  // If HOB list not found, assert
  //
  if (mHobList == NULL) {
    DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", EFI_INVALID_PARAMETER));
    ASSERT (FALSE);
  }

  ASSERT (mHobList != NULL);

  return mHobList;
}

/**
  Entry point for SlotDataUpdateDxeLightningRidgeEXECB4 driver.

  This function performs the following:
    1. Saves ImageHandle and SystemTable to global variables
    2. Validates all required system table pointers
    3. Initializes the HOB list
    4. Prints a debug trace indicating the driver has loaded
    5. Locates the slot configuration protocol
    6. Installs platform-specific slot configuration protocol interfaces

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

  @retval EFI_SUCCESS           The entry point executed successfully.
  @retval EFI_INVALID_PARAMETER A required protocol could not be located.
  @retval EFI_UNSUPPORTED       The platform is not supported.

**/
EFI_STATUS
EFIAPI
SlotDataUpdateDxeEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS   Status;
  VOID         *SlotProtocol;

  //
  // Save and validate ImageHandle
  //
  gImageHandle = ImageHandle;
  ASSERT (ImageHandle != NULL);

  //
  // Save and validate SystemTable
  //
  gSystemTable = SystemTable;
  ASSERT (SystemTable != NULL);

  //
  // Save and validate BootServices
  //
  gBootServices = SystemTable->BootServices;
  ASSERT (gBootServices != NULL);

  //
  // Save and validate RuntimeServices
  //
  gRuntimeServices = SystemTable->RuntimeServices;
  ASSERT (gRuntimeServices != NULL);

  //
  // Initialize debug print protocol
  //
  InitializeDebugProtocol ();

  //
  // Retrieve the HOB list (populates gHobList global)
  //
  GetHobListInternal (ImageHandle);

  //
  // Print driver load trace message
  //
  UbaDebugPrint (
    0x80000000,
    "UBA:SlotDataUpdate-TypeLightningRidgeEXECB3\n"
    );

  //
  // Locate the slot configuration protocol to install onto
  //
  Status = gBootServices->LocateProtocol (
                            &gLightningRidgeExEcB4SlotProtocolGuid,
                            NULL,
                            &SlotProtocol
                            );
  if (!EFI_ERROR (Status)) {
    //
    // Install first slot configuration (32 bytes of slot table data)
    // Protocol GUID: LIGHTNING_RIDGE_EX_EC_B4_SLOT_TABLE_PROTOCOL_GUID
    // Interface:     mSlotTableConfig1
    //
    Status = ((EFI_SLOT_PROTOCOL *)SlotProtocol)->InstallProtocol (
                                                    SlotProtocol,
                                                    &gLightningRidgeExEcB4SlotTableProtocolGuid,
                                                    (VOID *)&mSlotTableConfig1,
                                                    32
                                                    );
  }

  return Status;
}

/**
  Standard UEFI driver entry point.

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

  @retval EFI_SUCCESS           The driver loaded successfully.
  @retval other                 An error occurred during driver initialization.

**/
EFI_STATUS
EFIAPI
_ModuleEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  return SlotDataUpdateDxeEntryPoint (ImageHandle, SystemTable);
}