/** @file
  SlotDataUpdateDxeLightningRidgeEXECB3.c - Slot Data Update DXE Driver

  This driver implements the SlotDataUpdate functionality for the Lightning
  Ridge EX EC B3 platform using the UBA (Universal BIOS Agent) framework.
  It reads Platform Slot Table (PSLT) configuration from HOBs stored in
  the UEFI Configuration Table and the UBA HOB Database Protocol.

  The driver registers platform slot information that defines PCIe slot
  mapping for the motherboard: which physical slots map to which PCIe
  root ports, their hotplug capabilities, and other slot-specific attributes.

  File locations (from debug strings):
    - MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.c
    - MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.c
    - MdePkg/Library/DxeHobLib/HobLib.c
    - MdePkg/Library/BaseLib/Unaligned.c

  UEFI Phase: DXE (Driver Execution Environment)
  Protocol Dependencies:
    - UBA HOB Database Protocol (custom)
    - EFI_HOB_LIST_GUID in Configuration Table

  Copyright (c) 2025, Lenovo. All rights reserved.
  SPDX-License-Identifier: BSD-2-Clause-Patent
**/

#include "SlotDataUpdateDxeLightningRidgeEXECB3.h"

// ============================================================================
// Global Variables (EFI System Table globals, matched to BootServicesTableLib)
// ===========================================================================/

///
/// The EFI image handle for this driver.
/// Stored from ImageHandle parameter of ModuleEntryPoint.
/// @{ 0x0000 in .data, initialized by entry point
EFI_HANDLE              gImageHandle = NULL;
/// @}

///
/// The EFI System Table pointer for this driver.
/// Stored from SystemTable parameter of ModuleEntryPoint.
/// @{ 0x0008 in .data (relative), initialized by entry point
EFI_SYSTEM_TABLE       *gSystemTable = NULL;
/// @}

///
/// The EFI Boot Services Table pointer, cached from SystemTable->BootServices.
/// @{ 0x0010 in .data (relative), initialized by entry point
EFI_BOOT_SERVICES      *gBootServices = NULL;
/// @}

///
/// The EFI Runtime Services Table pointer, cached from SystemTable->RuntimeServices.
/// @{ 0x0018 in .data (relative), initialized by entry point
EFI_RUNTIME_SERVICES   *gRuntimeServices = NULL;
/// @}

///
/// Cached HOB list pointer from the EFI Configuration Table.
/// Found by scanning ConfigurationTable for EFI_HOB_LIST_GUID.
/// @{ At address 0xC28 in .data, zero-initialized
VOID                    *gHobList = NULL;
/// @}

///
/// Cached UBA HOB Database Protocol interface pointer.
/// Retrieved via gBS->LocateProtocol() on first access.
/// @{ At address 0xC30 in .data, zero-initialized
VOID                    *gUbaHobProtocol = NULL;
/// @}

// ============================================================================
// Global Data: PSLT Structure Templates and HOB Output Buffers
// ===========================================================================/

///
/// EFI_HOB_LIST_GUID value stored for Configuration Table comparison.
/// {7739F24C-93D7-11D4-9A3A-0090273FC14D}
/// Used by GetHobListFromConfigTable() to find the HOB list in the
/// System Table's ConfigurationTable array.
/// @{ At address 0xBA0 in .data (in .rdata section, read-only)
EFI_GUID  gEfiHobListGuid = EFI_HOB_LIST_GUID;
/// @}

///
/// GUID for the UBA HOB Database Protocol.
/// Used with gBS->LocateProtocol() to find the HOB database interface.
/// {E03E0D46-5263-4845-B0A4-58D57B3177E2}
/// @{ At address 0xB70 in .data (in .rdata section, read-only)
EFI_GUID  gUbaHobDatabaseProtocolGuid = UBA_HOB_DATABASE_PROTOCOL_GUID;
/// @}

///
/// GUID for the PSLT (Platform Slot Table) HOB entry (40-byte output).
/// {226763AE-972C-4E3C-80D1-73B25E8CBBA3}
/// @{ At address 0xB80 in .data (in .rdata section, read-only)
EFI_GUID  gLightningRidgeExecb3PsltHobGuid = LIGHTNING_RIDGE_EXECB3_PSLT_HOB_GUID;
/// @}

///
/// GUID for the variant HOB entry (32-byte output).
/// {B93613E1-48F0-4B32-B3A8-4FEDFC7C1365}
/// @{ At address 0xB90 in .data (in .rdata section, read-only)
EFI_GUID  gLightningRidgeExecb3VariantHobGuid = LIGHTNING_RIDGE_EXECB3_VARIANT_HOB_GUID;
/// @}

///
/// Output buffer for the variant HOB query (32 bytes).
/// Receives slot table data from the first HOB GUID query.
/// Contains PSLT header data with platform-specific slot configuration.
/// @{ At address 0xBC0 in .data (actually .rdata in original PE layout)
PLATFORM_SLOT_TABLE  gSlotTableData = {
  .Signature = PSLT_SIGNATURE,    ///< "PSLT" at offset +0x00
  .Version   = PSLT_VERSION,      ///< Version 1 at offset +0x04
  .Reserved1 = 0,                 ///< At offset +0x08
  .Reserved2 = 0x4B0,            ///< At offset +0x0C (platform function table base?)
  .Reserved3 = 1,                 ///< Count field at offset +0x10
  .Count     = 1                  ///< Number of slot entries at offset +0x14
};
/// @}

///
/// Output buffer for the extended PSLT HOB query (40 bytes).
/// Contains PSLT header plus additional slot data.
/// @{ At address 0xBE0 in .data (actually .rdata in original PE layout)
UINT8  gSlotTableDataEx[40] = {
  /// Same PSLT header as gSlotTableData (32 bytes)
  [0x00 ... 0x1F] = 0x00,
  /// Extended data at offset +0x20
  [0x20] = 0xB4, [0x21] = 0x04, [0x22 ... 0x27] = 0x00   ///< Additional field
};
/// @}


// ============================================================================
// Function Implementations
// ============================================================================

/**
  Returns platform type code for this driver's target.

  Lightning Ridge EX EC B3 returns platform type 2.
  This function is referenced as a callback at address 0x4B0 in the
  PSLT data structure's function table.

  @return  Platform type code (2).
**/
UINT8
EFIAPI
GetPlatformType (
  VOID
  )
{
  //
  // Platform type 2 = Lightning Ridge EX EC B3
  //
  return 2;
}


/**
  Passthrough function for platform callback.

  Returns the value passed in a2 unchanged. This is used when no
  transformation is needed for the platform callback chain.

  @param[in]  Context   Callback context (unused)
  @param[in]  Value     Input value to pass through

  @return  Value unchanged.
**/
UINT8
EFIAPI
PlatformPassthrough (
  IN VOID   *Context,
  IN UINT8  Value
  )
{
  //
  // Simple identity function - return the input value
  //
  return Value;
}


/**
  Locates and caches the UBA HOB Database Protocol.

  This function implements a TPL (Task Priority Level) guard to ensure
  protocol location is only attempted when the current TPL is at or
  below TPL_NOTIFY (16). If the TPL is higher, LocateProtocol could
  potentially block or deadlock.

  Protocol is only located once and cached in gUbaHobProtocol for
  subsequent calls.

  On the first call:
    1. Raises TPL to TPL_HIGH_LEVEL (31) via gBS->RaiseTPL()
    2. If OldTpl <= TPL_NOTIFY (16):
       a. Calls gBS->LocateProtocol() with gUbaHobDatabaseProtocolGuid
       b. Caches result in gUbaHobProtocol
    3. Restores TPL via gBS->RestoreTPL(OldTpl)

  @return  Pointer to the UBA_HOB_DATABASE_PROTOCOL interface,
           or NULL if not found / TPL too high.

  @note  The first 8 bytes of the protocol interface are reserved.
         Debug print is at offset +0x08.
         GetNextGuidHob is at offset +0x10.
**/
UBA_HOB_DATABASE_PROTOCOL *
EFIAPI
GetUbaHobDatabaseProtocol (
  VOID
  )
{
  UBA_HOB_DATABASE_PROTOCOL  *Protocol;
  EFI_TPL                     OldTpl;

  //
  // Return cached protocol if already found
  //
  if (gUbaHobProtocol != NULL) {
    return (UBA_HOB_DATABASE_PROTOCOL *)gUbaHobProtocol;
  }

  //
  // TPL guard: only locate protocol if current TPL <= TPL_NOTIFY (16)
  // Raise to TPL_HIGH_LEVEL (31) to check, then restore
  //
  OldTpl = gBootServices->RaiseTPL (TPL_HIGH_LEVEL);
  gBootServices->RestoreTPL (OldTpl);

  if (OldTpl <= TPL_NOTIFY) {
    //
    // Locate the UBA HOB Database Protocol
    //
    gBootServices->LocateProtocol (
                     &gUbaHobDatabaseProtocolGuid,
                     NULL,
                     &gUbaHobProtocol
                     );
  }

  return (UBA_HOB_DATABASE_PROTOCOL *)gUbaHobProtocol;
}


/**
  Uses the UBA HOB Database Protocol to log a debug message.

  If the UBA protocol has been located, calls the debug print function
  at offset +0x08 in the protocol interface. Otherwise, silently does
  nothing.

  @param[in]  DebugLevel  Debug message severity level (e.g., UBA_DEBUG_LEVEL_INFO).
  @param[in]  Format      Format string.
  @param[in]  ...         Variable arguments.

  @return  Status code from the debug print function (typically 0 = success),
           or 4 (default) if protocol is not available.
**/
UINT8
EFIAPI
UbaDebugPrint (
  IN UINTN       DebugLevel,
  IN CONST CHAR8 *Format,
  ...
  )
{
  UBA_HOB_DATABASE_PROTOCOL  *Protocol;
  UINTN                       Result;
  VA_LIST                     VaList;
  UINT64                      DebugFlags;

  Protocol = GetUbaHobDatabaseProtocol ();
  if (Protocol == NULL) {
    return 4;
  }

  //
  // The debug print function at offset +0x08 in the protocol has signature:
  //   UINTN (*)(UINTN DebugLevel, CONST CHAR8 *Format, VA_LIST Args)
  //
  // It uses a debug flags mask: if (DebugFlags & DebugLevel) is non-zero,
  // the message is printed. Otherwise it is supressed.
  //
  DebugFlags = 0x80000004;  ///< Default mask: allow INFO and ERROR levels

  if ((DebugFlags & DebugLevel) != 0) {
    VA_START (VaList, Format);
    //
    // Call protocol debug function (UINT64 function pointer at offset +0x08
    // to match x64 calling convention padding)
    //
    Result = ((UINT64 (*)(VOID *, UINTN, CONST CHAR8 *, VA_LIST))
              (((UINT64 *)Protocol)[1])) (
               Protocol,
               DebugLevel,
               Format,
               VaList
               );
    VA_END (VaList);
    return (UINT8)Result;
  }

  return 4;
}


/**
  Assert and deadloop handler.

  Prints assert information containing the file name, line number, and
  condition text, then enters an infinite loop (deadloop). Called by the
  ASSERT() and ASSERT_EFI_ERROR() macros.

  @param[in]  FileName    Source file name string
  @param[in]  LineNumber  Line number in source file
  @param[in]  AssertText  Assert condition text
**/
VOID
EFIAPI
UbaAssert (
  IN CHAR8  *FileName,
  IN UINTN  LineNumber,
  IN CHAR8  *AssertText
  )
{
  //
  // Log assert with UBA debug protocol
  //
  UbaDebugPrint (
    UBA_DEBUG_LEVEL_STATUS,
    "\nASSERT_EFI_ERROR (Status = %r)\n",
    0x800000000000000EuLL  ///< EFI_ABORTED as format argument
    );

  //
  // Deadloop - CPU will spin here forever
  // This matches the original decompiled behavior
  //
  while (TRUE) {
    CpuDeadLoop ();
  }
}


/**
  Scans the EFI System Table ConfigurationTable for an entry whose
  GUID matches gEfiHobListGuid, and caches the HOB list pointer.

  The EFI System Table contains a ConfigurationTable array where each
  entry is 24 bytes: a 16-byte GUID followed by an 8-byte interface
  pointer. This function walks that array looking for EFI_HOB_LIST_GUID.

  Steps:
    1. If gHobList is already non-NULL, return it immediately (cache hit)
    2. Clear gHobList to NULL
    3. If SystemTable->NumberOfTableEntries is 0, ASSERT and deadloop
    4. Walk ConfigurationTable entries:
       - Each entry is 24 bytes: GUID(16) + Interface(8)
       - Compare GUID by splitting into two 8-byte halves
       - The two template values come from gEfiHobListGuid:
         [0:7]  = first 8 bytes of the GUID
         [8:15] = second 8 bytes of the GUID
       - If match found, save Interface[16] pointer into gHobList
    5. If gHobList is still NULL after walk, ASSERT and deadloop

  This is functionally equivalent to the DXE-phase GetHobList()
  from DxeHobLib but implemented as an inline scan of the
  ConfigurationTable rather than using a library call.

  @return  Pointer to the cached HOB list (gHobList)
**/
VOID *
EFIAPI
GetHobListFromConfigTable (
  VOID
  )
{
  UINTN   Index;
  UINTN   TableEntryCount;
  VOID   *ConfigTable;

  //
  // Return cached HOB list if already found
  //
  if (gHobList != NULL) {
    return gHobList;
  }

  //
  // Get Configuration Table info from SystemTable
  // SystemTable + 0x68 = NumberOfTableEntries
  // SystemTable + 0x70 = ConfigurationTable pointer
  //
  gHobList = NULL;

  TableEntryCount = gSystemTable->NumberOfTableEntries;
  ConfigTable     = gSystemTable->ConfigurationTable;

  if (TableEntryCount == 0) {
    //
    // No configuration table entries - this is an error
    // Log assert and enter deadloop
    //
    UbaDebugPrint (
      UBA_DEBUG_LEVEL_STATUS,
      "\nASSERT_EFI_ERROR (Status = %r)\n",
      0x800000000000000EuLL  ///< EFI_ABORTED
      );
    UbaAssert (
      "e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
      54,
      "!EFI_ERROR (Status)"
      );
    return gHobList;
  }

  //
  // Walk the Configuration Table entries looking for EFI_HOB_LIST_GUID
  // Each entry is 24 bytes: GUID(16) + Interface pointer(8)
  //
  for (Index = 0; Index < TableEntryCount; Index++) {
    //
    // Calculate current entry address (24 bytes per entry)
    //
    VOID  *Entry = (UINT8 *)ConfigTable + (Index * 24);

    //
    // Check if this entry's GUID matches EFI_HOB_LIST_GUID
    // Compare by splitting both GUIDs into two 8-byte halves
    //
    if (CompareGuidQwords (Entry, &gEfiHobListGuid, (UINT8 *)&gEfiHobListGuid + 8)) {
      //
      // Found the HOB list - cache the interface pointer at offset +0x10
      //
      gHobList = *(VOID **)((UINT8 *)Entry + 16);
      break;
    }
  }

  //
  // If HOB list was not found, ASSERT
  //
  if (gHobList == NULL) {
    UbaAssert (
      "e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
      55,
      "mHobList != ((void *) 0)"
      );
  }

  return gHobList;
}


/**
  Compares two GUIDs by matching their first and second 8-byte halves.

  Since the ConfigurationTable entries store GUIDs as 16-byte contiguous
  blobs, this function efficiently compares GUIDs by splitting into
  two QWORD comparisons instead of byte-by-byte or using full GUID
  comparison protocol.

  @param[in]  EntryAddr   Address of the ConfigurationTable entry (or any
                          structure with 16 bytes of GUID data at start)
  @param[in]  Guid1       Pointer to 8 bytes representing the first half
                          (bytes 0-7) of the target GUID
  @param[in]  Guid2       Pointer to 8 bytes representing the second half
                          (bytes 8-15) of the target GUID

  @retval TRUE   GUID at EntryAddr matches {Guid1, Guid2}
  @retval FALSE  GUID does not match
**/
BOOLEAN
EFIAPI
CompareGuidQwords (
  IN VOID   *EntryAddr,
  IN VOID   *Guid1,
  IN VOID   *Guid2
  )
{
  UINT64  EntryQword0;
  UINT64  EntryQword1;
  UINT64  TemplateQword0;
  UINT64  TemplateQword1;

  //
  // Read unaligned QWORDs from both entries
  //
  EntryQword0    = ReadUnaligned64 (EntryAddr);
  EntryQword1    = ReadUnaligned64 ((UINT8 *)EntryAddr + 8);
  TemplateQword0 = ReadUnaligned64 (Guid1);
  TemplateQword1 = ReadUnaligned64 (Guid2);

  //
  // Both halves must match
  //
  return (BOOLEAN)(EntryQword0 == TemplateQword0 && EntryQword1 == TemplateQword1);
}


/**
  Reads an unaligned 64-bit value from memory with NULL pointer check.

  This is functionally equivalent to ReadUnaligned64() from BaseLib.
  Asserts if Buffer is NULL.

  @param[in]  Buffer  Pointer to the 8-byte value to read (must not be NULL)

  @return  64-bit value at the given address, read unaligned.
**/
UINT64
EFIAPI
ReadUnaligned64 (
  IN CONST VOID *Buffer
  )
{
  //
  // NULL pointer check (asserts and deadloops if NULL)
  //
  if (Buffer == NULL) {
    UbaAssert (
      "e:\\hs\\MdePkg\\Library\\BaseLib\\Unaligned.c",
      192,
      "Buffer != ((void *) 0)"
      );
  }

  //
  // Read 8 bytes unaligned
  //
  return *(volatile UINT64 *)Buffer;
}


/**
  Main entry point for the SlotDataUpdateDxeLightningRidgeEXECB3 driver.

  This is the core initialization routine for the SlotDataUpdate driver.
  It performs the following operations in sequence:

  Phase 1 - Initialize EFI Service Pointers:
    1a. Save ImageHandle to gImageHandle global (NULL check)
    1b. Save SystemTable to gSystemTable global (NULL check)
    1c. Cache BootServices from SystemTable (NULL check)
    1d. Cache RuntimeServices from SystemTable (NULL check)

  Phase 2 - Initialize HOB List Access:
    2a. Call GetHobListFromConfigTable() to locate and cache the HOB list
    2b. This scans SystemTable->ConfigurationTable for EFI_HOB_LIST_GUID

  Phase 3 - Platform Identification:
    3a. Debug print "UBA:SlotDataUpdate-TypeLightningRidgeEXECB3" to
        identify this module's platform target in the UBA framework
    3b. This distinguishes EC B3 from other board revisions (EC B1, B2, B4, etc.)

  Phase 4 - Query UBA HOB Protocol for Slot Data:
    4a. Locate the UBA HOB Database Protocol via gBS->LocateProtocol()
    4b. Call protocol.GetNextGuidHob() with:
        - gLightningRidgeExecb3VariantHobGuid => output to gSlotTableData (32 bytes)
        - gLightningRidgeExecb3PsltHobGuid    => output to gSlotTableDataEx (40 bytes)

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

  @return  EFI_SUCCESS           The driver initialized successfully.
  @return  EFI_INVALID_PARAMETER One of the HOB database queries failed.
  @return  EFI_NOT_FOUND         UBA HOB Database Protocol not found.
**/
EFI_STATUS
EFIAPI
SlotDataUpdateEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS                   Status;
  UBA_HOB_DATABASE_PROTOCOL   *UbaHobProtocol;

  //
  // PHASE 1: Initialize EFI Service Pointers
  //

  //
  // Save ImageHandle - required for UEFI driver registration
  // NULL check via ASSERT (from UefiBootServicesTableLib)
  //
  gImageHandle = ImageHandle;
  if (gImageHandle == NULL) {
    UbaAssert (
      "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
      51,
      "gImageHandle != ((void *) 0)"
      );
  }

  //
  // Save SystemTable pointer
  // NULL check via ASSERT
  //
  gSystemTable = SystemTable;
  if (gSystemTable == NULL) {
    UbaAssert (
      "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
      57,
      "gST != ((void *) 0)"
      );
  }

  //
  // Cache Boot Services table pointer
  // NULL check via ASSERT
  //
  gBootServices = SystemTable->BootServices;
  if (gBootServices == NULL) {
    UbaAssert (
      "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
      63,
      "gBS != ((void *) 0)"
      );
  }

  //
  // Cache Runtime Services table pointer
  // NULL check via ASSERT
  //
  gRuntimeServices = SystemTable->RuntimeServices;
  if (gRuntimeServices == NULL) {
    UbaAssert (
      "e:\\hs\\MdePkg\\Library\\UefiRuntimeServicesTableLib\\UefiRuntimeServicesTableLib.c",
      47,
      "gRT != ((void *) 0)"
      );
  }

  //
  // PHASE 2: Initialize HOB List Access
  // Locate the HOB list from the System Table Configuration Table
  // This is needed for subsequent HOB queries
  //
  GetHobListFromConfigTable ();

  //
  // PHASE 3: Platform Identification
  // Print platform identifier string for UBA framework debugging
  //
  UbaDebugPrint (
    UBA_DEBUG_LEVEL_INFO,
    "UBA:SlotDataUpdate-TypeLightningRidgeEXECB3\n"
    );

  //
  // PHASE 4: Query UBA HOB Protocol for Slot Data
  //

  //
  // Locate the UBA HOB Database Protocol
  // This protocol provides access to platform-specific HOBs
  //
  Status = gBootServices->LocateProtocol (
                            &gUbaHobDatabaseProtocolGuid,
                            NULL,
                            (VOID **)&UbaHobProtocol
                            );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Query variant HOB (32-byte output buffer)
  // This retrieves platform slot table variant data that defines
  // the PCIe slot configuration for this specific board revision.
  //
  Status = UbaHobProtocol->GetNextGuidHob (
                             UbaHobProtocol,
                             &gLightningRidgeExecb3VariantHobGuid,
                             &gSlotTableData,
                             32
                             );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Query PSLT HOB (40-byte output buffer)
  // This retrieves extended slot table data with additional
  // platform-specific configuration fields.
  //
  Status = UbaHobProtocol->GetNextGuidHob (
                             UbaHobProtocol,
                             &gLightningRidgeExecb3PsltHobGuid,
                             &gSlotTableDataEx,
                             40
                             );

  return Status;
}