/** @file
  SetupConfigUpdateDxeLightningRidgeEXECB4 — UBA Slot Data Update DXE driver.

  This DXE driver is part of the UBA (Universal BIOS Architecture) slot
  configuration framework. It installs slot data update configuration
  protocols for the LightningRidge EX EC B4 platform, allowing the
  system firmware to apply platform-specific slot configuration data
  during the boot process.

  The driver performs the following operations:
  * Initializes the UEFI Boot/Runtime Services table pointers.
  * Locates the HOB (Hand-Off Block) list from the system table.
  * Registers the UBA slot data update configuration for LightningRidge
    EX EC B4 via the UBA configuration protocol.

  Copyright (c) 2025, Insyde Software Corp. All rights reserved.

  SPDX-License-Identifier: BSD-2-Clause-Patent
**/

#include <Uefi.h>
#include <Library/DebugLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/HobLib.h>
#include <Library/BaseLib.h>
#include <Library/UefiLib.h>
#include "SetupConfigUpdateDxeLightningRidgeEXECB4.h"

//
// Globals for UEFI boot/runtime services.
//
EFI_HANDLE            gImageHandle = NULL;
EFI_SYSTEM_TABLE      *gST         = NULL;
EFI_BOOT_SERVICES     *gBS         = NULL;
EFI_RUNTIME_SERVICES  *gRT         = NULL;

//
// HOB list pointer, cached for use by HOB library functions.
//
VOID  *mHobList = NULL;

//
// UBA protocol interface pointer, obtained via LocateProtocol.
//
VOID  *gUbaProtocolInterface = NULL;

//
// UBA debug print routing function pointer; obtained via
// LocateProtocol using the UBA debug protocol GUID.
//
UBA_DEBUG_PRINT  gUbaDebugPrint = NULL;

//
// GUID definitions
//
EFI_GUID  gUbaSlotDataUpdateConfigProtocolGuid =
  UBA_SLOT_DATA_UPDATE_CONFIG_PROTOCOL_GUID;

EFI_GUID  gUbaSlotDataUpdateProtocolGuid =
  UBA_SLOT_DATA_UPDATE_PROTOCOL_GUID_EXECB4;

EFI_GUID  gUbaSlotDataUpdateConfigTableGuid =
  UBA_SLOT_DATA_UPDATE_CONFIG_TABLE_GUID;

EFI_GUID  gUbaSlotDataGuid =
  UBA_SLOT_DATA_GUID_EXECB4;

EFI_GUID  gUbaDebugProtocolGuid =
  UBA_DEBUG_PROTOCOL_GUID;

//
// Slot data update configuration table data.
//
// This structure is published via the UBA protocol to describe the
// slot data update configuration for the LightningRidge EX EC B4
// platform.
//
UBA_SLOT_DATA_UPDATE_CONFIG  mSlotDataUpdateConfig = {
  .Revision           = 1,
  .Reserved           = { 0, 0, 0 },
  .SlotDataConfig     = (VOID *)(UINTN)0xBB0,
  .SlotDataConfigSize = sizeof (UBA_SLOT_DATA_UPDATE_CONFIG),
  .SlotDataUpdateTable     = NULL,
  .SlotDataUpdateTableSize = 0,
};

//
// Slot data payload descriptor GUID table
//
// Each entry in this table describes a slot data payload by GUID and
// size, which is consumed by the UBA framework to map platform-specific
// slot data.
//
typedef struct {
  EFI_GUID  Guid;
  UINT32    DataSize;
} SLOT_DATA_PAYLOAD_DESCRIPTOR;

SLOT_DATA_PAYLOAD_DESCRIPTOR  mSlotDataPayloads[] = {
  { UBA_SLOT_DATA_UPDATE_CONFIG_TABLE_GUID, 32 },
  { UBA_SLOT_DATA_GUID_EXECB4,              40 },
};

//
// Forward declarations.
//
EFI_STATUS
EFIAPI
SetupConfigUpdateDxeLightningRidgeEXECB4EntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  );

/**
  Resolves the HOB list pointer from the system table configuration table.

  Iterates through the system table's configuration tables to locate the
  HOB list. The HOB list is published by the PEI phase and consumed by
  DXE drivers via the HOB Library.

  @retval Pointer to the start of the HOB list.
**/
VOID *
EFIAPI
GetHobList (
  VOID
  )
{
  EFI_STATUS            Status;
  UINTN                 Index;
  EFI_CONFIGURATION_TABLE  *ConfigTable;
  VOID                  *HobList;

  //
  // Return cached HOB list if already resolved.
  //
  if (mHobList != NULL) {
    return mHobList;
  }

  HobList = NULL;
  ConfigTable = gST->ConfigurationTable;

  if (gST->NumberOfTableEntries > 0) {
    for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {
      if (CompareGuid (&ConfigTable[Index].VendorGuid, &gUbaSlotDataUpdateProtocolGuid)) {
        HobList = ConfigTable[Index].VendorTable;
        mHobList = HobList;
        break;
      }
    }
  }

  //
  // ASSERT if no HOB list entry was found matching the expected GUID.
  //
  if (HobList == NULL) {
    DEBUG ((DEBUG_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
    ASSERT_EFI_ERROR (Status);
    ASSERT (mHobList != NULL);
  }

  return HobList;
}

/**
  Locates and caches the UBA protocol interface pointer.

  Uses the UBA protocol GUID to locate the protocol interface via
  UEFI Boot Services. On success, stores the interface in the
  module-global gUbaProtocolInterface variable for subsequent use
  by the UBA library functions.

  @retval Pointer to the UBA protocol interface, or NULL if not found.
**/
VOID *
EFIAPI
LocateUbaProtocol (
  VOID
  )
{
  EFI_STATUS  Status;

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

  Status = gBS->LocateProtocol (
                  &gUbaSlotDataUpdateConfigProtocolGuid,
                  NULL,
                  &gUbaProtocolInterface
                  );
  if (EFI_ERROR (Status)) {
    gUbaProtocolInterface = NULL;
  }

  return gUbaProtocolInterface;
}

/**
  UBA debug print hook.

  Routes debug output through the UBA debug protocol if available.
  Falls back to a no-op if the protocol is not installed.

  @param[in]  ErrorLevel  Debug error level mask.
  @param[in]  Format      Format string.
  @param[in]  ...         Variable arguments for format string.
**/
VOID
EFIAPI
UbaDebugPrint (
  IN UINTN        ErrorLevel,
  IN CONST CHAR8  *Format,
  ...
  )
{
  VA_LIST         Marker;
  UBA_DEBUG_PRINT DebugPrint;

  DebugPrint = LocateUbaDebugProtocol ();
  if (DebugPrint == NULL) {
    return;
  }

  VA_START (Marker, Format);
  DebugPrint (ErrorLevel, Format, &Marker);
  VA_END (Marker);
}

/**
  Locates the UBA debug print function via protocol lookup.

  @retval Pointer to the UBA debug print function, or NULL.
**/
UBA_DEBUG_PRINT
EFIAPI
LocateUbaDebugProtocol (
  VOID
  )
{
  EFI_STATUS  Status;

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

  Status = gBS->LocateProtocol (
                  &gUbaDebugProtocolGuid,
                  NULL,
                  (VOID **)&gUbaDebugPrint
                  );
  if (EFI_ERROR (Status)) {
    gUbaDebugPrint = NULL;
  }

  return gUbaDebugPrint;
}

/**
  Checks whether a HOB entry matches the expected platform GUID.

  Compares two GUIDs: the platform HOB GUID and the GUID at the
  supplied address in the HOB list. Used to locate the correct
  HOB entry for the LightningRidge EX EC B4 platform.

  @param[in]  PlatformHobGuid  Pointer to the platform HOB GUID.
  @param[in]  CandidateHobEntry  Pointer to a candidate HOB entry GUID.

  @retval TRUE   The GUIDs match.
  @retval FALSE  The GUIDs do not match.
**/
BOOLEAN
EFIAPI
IsPlatformHob (
  IN CONST EFI_GUID  *PlatformHobGuid,
  IN CONST VOID      *CandidateHobEntry
  )
{
  EFI_GUID  *CandidateGuid;
  EFI_GUID  *PlatformGuid;
  EFI_GUID  *SubGuid1;
  EFI_GUID  *SubGuid2;

  //
  // Read unaligned GUID values from the HOB entries.
  //
  CandidateGuid = (EFI_GUID *)ReadUnaligned64 ((UINT64 *)CandidateHobEntry);
  PlatformGuid  = (EFI_GUID *)ReadUnaligned64 ((UINT64 *)PlatformHobGuid);

  if (CandidateGuid == NULL || PlatformGuid == NULL) {
    DEBUG ((DEBUG_ERROR, "Buffer != ((void *) 0)\n"));
    ASSERT (CandidateGuid != NULL);
    ASSERT (PlatformGuid != NULL);
    return FALSE;
  }

  SubGuid1 = (EFI_GUID *)ReadUnaligned64 ((UINT64 *)((UINT8 *)CandidateHobEntry + 8));
  SubGuid2 = (EFI_GUID *)ReadUnaligned64 ((UINT64 *)((UINT8 *)PlatformHobGuid + 8));

  return CompareGuid (CandidateGuid, PlatformGuid) &&
         CompareGuid (SubGuid1, SubGuid2);
}

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

  Wrapper around the BaseLib ReadUnaligned64 function with ASSERT
  protection.

  @param[in]  Buffer  Pointer to the unaligned buffer.

  @return The 64-bit value read from the buffer.
**/
UINT64
EFIAPI
ReadUnaligned64OrAssert (
  IN CONST UINT64  *Buffer
  )
{
  if (Buffer == NULL) {
    DEBUG ((DEBUG_ERROR, "Buffer != ((void *) 0)\n"));
    ASSERT (Buffer != NULL);
  }

  return ReadUnaligned64 (Buffer);
}

/**
  Platform-specific debug assertion handler.

  If the UBA debug protocol is available, routes the assertion
  failure message through it; otherwise discards the message.

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

  DebugPrint = LocateUbaDebugProtocol ();
  if (DebugPrint != NULL) {
    DebugPrint (
      DEBUG_ERROR,
      "%a(%d): %a\n",
      (VA_LIST)&FileName
      );
  }
}

//
// Slot data update payload configuration
//
// The following table defines the GUID and size of each slot data
// payload that this driver registers with the UBA protocol.
//
// Payload 1: UBA Slot Data Update Config Table  — 32 bytes
// Payload 2: UBA Slot Data GUID EXECB4          — 40 bytes
//
// These payloads are installed into the UBA configuration database
// during the entry point execution.
//

/**
  Entry point for the SetupConfigUpdateDxeLightningRidgeEXECB4 driver.

  This function is the module entry point called by the UEFI core
  after the image is loaded. It performs the following initialization:
  1. Caches ImageHandle, SystemTable, BootServices, and RuntimeServices.
  2. Resolves the HOB list from the system configuration table.
  3. Locates the UBA protocol interface.
  4. Installs the slot data update configuration into the UBA protocol
     database, publishing two payload entries:
     - UBA Slot Data Update Config Table (32 bytes)
     - UBA Slot Data GUID EXECB4 (40 bytes)

  @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 initialized successfully.
  @retval EFI_INVALID_PARAMETER ImageHandle or SystemTable was NULL.
  @retval EFI_NOT_FOUND         Required protocol or HOB data was not found.
  @retval Other                 Error from protocol installation.
**/
EFI_STATUS
EFIAPI
SetupConfigUpdateDxeLightningRidgeEXECB4EntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS                    Status;
  UINTN                         Index;
  UINTN                         PayloadCount;
  UBA_SLOT_DATA_UPDATE_CONFIG   *ConfigData;
  UBA_PROTOCOL_INTERFACE        *UbaProtocol;

  //
  // Step 1: Initialize UEFI service table pointers.
  //
  gImageHandle = ImageHandle;
  ASSERT (gImageHandle != NULL);

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

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

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

  //
  // Step 2: Resolve the HOB list. Required by the HOB library for
  //         subsequent HOB traversal.
  //
  GetHobList ();

  //
  // Step 3: Log the slot data update registration event.
  //
  UbaDebugPrint (
    DEBUG_INFO,
    "UBA:SlotDataUpdate-TypeLightningRidgeEXECB4\n"
    );

  //
  // Step 4: Locate the UBA protocol interface.
  //
  Status = gBS->LocateProtocol (
                  &gUbaSlotDataUpdateConfigProtocolGuid,
                  NULL,
                  (VOID **)&UbaProtocol
                  );
  if (!EFI_ERROR (Status)) {
    //
    // Step 5: Install slot data update payloads into the UBA database.
    //
    PayloadCount = sizeof (mSlotDataPayloads) / sizeof (mSlotDataPayloads[0]);

    for (Index = 0; Index < PayloadCount; Index++) {
      Status = UbaProtocol->SetData (
                              UbaProtocol,
                              &mSlotDataPayloads[Index].Guid,
                              &mSlotDataUpdateConfig,
                              mSlotDataPayloads[Index].DataSize
                              );
    }
  }

  return Status;
}