/** @file
  UsbOcUpdateDxeLightningRidgeEXECB3 -- USB Overcurrent Mapping Driver

  This DXE driver implements USB Overcurrent (OC) mapping configuration for
  the LightningRidge EX EC B3 platform. It registers OC mapping data with
  the UBA (Universal BIOS Architecture) configuration database protocol.

  The OC mapping tables describe how physical USB ports on the platform are
  associated with overcurrent protection channels. The data is consumed by
  the platform USB controller driver during initialization to properly
  configure OC detection hardware.

  The driver performs the following during initialization:
    1. Initializes UEFI Boot Services and Runtime Services table pointers
    2. Retrieves the UBA platform HOB for board type validation
    3. Locates the UBA Configuration Database protocol
    4. Registers the USB OC configuration data for this platform

  Reconstructed from binary analysis of the original UEFI driver.
  Source: UsbOcUpdateDxeLightningRidgeEXECB3.efi (SHA256: b440ac1d8dfa...)

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

**/

#include "UsbOcUpdateDxeLightningRidgeEXECB3.h"

//
// Global UEFI table pointers (filled in by entry point)
//
EFI_HANDLE          gImageHandle     = NULL;
EFI_SYSTEM_TABLE    *gSystemTable    = NULL;
EFI_BOOT_SERVICES   *gBootServices   = NULL;
EFI_RUNTIME_SERVICES *gRuntimeServices = NULL;

//
// Cached HOB list pointer
//
VOID  *gHobList = NULL;

//
// Cached UBA Configuration Database protocol pointer
//
UBA_CONFIG_DATABASE_PROTOCOL  *gUbaConfigDb = NULL;

//
// UBA configuration database protocol GUID
//
EFI_GUID  mUbaConfigDatabaseProtocolGuid = USB_OC_UBA_PROTOCOL_GUID;

//
// Parameter GUID for USB OC configuration
//
EFI_GUID  mUsbOcConfigGuid = USB_OC_PARAM_GUID;

//
// HOB GUID for locating UBA platform data
//
EFI_GUID  mUbaPlatformHobGuid = USB_OC_HOB_GUID;

///
/// PUSB table metadata
///
#define PUSB_TABLE_SIZE  0x48C

///
/// Combined UBA OC configuration data structure containing all mapping tables.
///
UBA_OC_CONFIG_DATA  mUsbOcConfigData = {
  //
  // USB port numbers -- logical index to physical port number mapping.
  // 0 = unused/unavailable port slot.
  //
  .PortNumber   = { 0, 1, 1, 2, 3, 3, 8, 8, 8, 8, 0, 0 },

  //
  // OC channel index per physical port.
  // 8 = no overcurrent channel (port not physically connected or no OC).
  //
  .OcIndex      = { 0, 1, 8, 8, 8, 2, 1, 2, 8, 8, 8, 4, 8, 4 },

  //
  // PUSB table header
  //
  .PusbSignature = PUSB_TABLE_SIGNATURE,
  .PusbVersion   = PUSB_TABLE_VERSION_EXECB3,
  .PusbSize      = PUSB_TABLE_SIZE,

  //
  // OC data mapping entries.
  // All entries identical: 0x01020007 encodes channel and attribute info.
  //
  .OcData        = {
    USB_OC_DATA_ENTRY,
    USB_OC_DATA_ENTRY,
    USB_OC_DATA_ENTRY,
    USB_OC_DATA_ENTRY,
  },
};


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

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

  @return The 64-bit value read from the given address.
**/
UINT64
EFIAPI
ReadUnaligned64 (
  IN CONST VOID  *Buffer
  )
{
  ASSERT (Buffer != NULL);

  //
  // Read the 64-bit value as two 32-bit halves to handle unaligned access.
  // The UEFI specification guarantees this is safe.
  //
  return *(CONST UINT64 *)Buffer;
}


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

  @param[in] HobGuid  Pointer to the GUID to compare against the UBA platform GUID.

  @retval TRUE   The GUIDs match (this is a UBA platform HOB).
  @retval FALSE  The GUIDs do not match.
**/
BOOLEAN
IsUbaPlatformHob (
  IN CONST EFI_GUID  *HobGuid
  )
{
  UINT64  GuidFirstHalf;
  UINT64  GuidSecondHalf;

  //
  // Compare the GUID as two 64-bit halves for efficiency.
  //
  GuidFirstHalf  = ReadUnaligned64 (HobGuid);
  GuidSecondHalf = ReadUnaligned64 ((UINT8 *)HobGuid + 8);

  return (GuidFirstHalf  == ReadUnaligned64 (&mUbaPlatformHobGuid)) &&
         (GuidSecondHalf == ReadUnaligned64 ((UINT8 *)&mUbaPlatformHobGuid + 8));
}


/**
  Retrieves the UBA platform HOB data pointer.

  Walks the system HOB list to find the UBA platform configuration HOB
  with the matching GUID, and caches the data pointer for later reuse.

  @return Pointer to the UBA platform HOB data, or NULL if not found.
**/
VOID *
GetUbaPlatformHob (
  VOID
  )
{
  EFI_HOB_GUID_TYPE  *GuidHob;

  //
  // Check for cached HOB list pointer
  //
  if (gHobList != NULL) {
    return gHobList;
  }

  //
  // Find the first GUID HOB that matches the UBA platform GUID.
  //
  GuidHob = GetFirstGuidHob (&mUbaPlatformHobGuid);
  if (GuidHob != NULL && IsUbaPlatformHob (&GuidHob->Name)) {
    //
    // Cache and return pointer to the HOB data portion (immediately after the GUID)
    //
    gHobList = (VOID *)(GuidHob + 1);
    return gHobList;
  }

  //
  // UBA platform HOB not found -- error condition
  //
  DEBUG ((DEBUG_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", EFI_NOT_FOUND));
  ASSERT (!EFI_ERROR (EFI_NOT_FOUND));

  return NULL;
}


/**
  Locates the UBA Configuration Database protocol.

  Uses the UEFI Boot Services LocateProtocol to find the UBA Config DB
  protocol, used for registering platform-specific configuration data.

  @return Pointer to the UBA_CONFIG_DATABASE_PROTOCOL interface,
          or NULL if the protocol was not found.
**/
UBA_CONFIG_DATABASE_PROTOCOL *
GetUbaConfigDb (
  VOID
  )
{
  EFI_STATUS  Status;

  //
  // Return cached pointer if already resolved
  //
  if (gUbaConfigDb != NULL) {
    return gUbaConfigDb;
  }

  //
  // Locate the UBA Configuration Database protocol
  //
  Status = gBootServices->LocateProtocol (
                            &mUbaConfigDatabaseProtocolGuid,
                            NULL,
                            (VOID **)&gUbaConfigDb
                            );
  if (EFI_ERROR (Status)) {
    gUbaConfigDb = NULL;
  }

  return gUbaConfigDb;
}


/**
  Returns three pointers used by the UBA callback to access platform
  configuration data:
    - Board ID / platform type identifier
    - USB port-to-OC index mapping table
    - Combined USB OC configuration data

  @param[out] BoardId     Pointer to receive the board/platform ID.
  @param[out] PortMapping Pointer to receive the USB port mapping table.
  @param[out] OcConfig    Pointer to receive the USB OC configuration data.

  @retval EFI_SUCCESS  The pointers were successfully returned.
**/
EFI_STATUS
GetUsbOcConfigPointers (
  OUT UINT32            **BoardId,
  OUT UINT32            **PortMapping,
  OUT UBA_OC_CONFIG_DATA **OcConfig
  )
{
  *BoardId    = mUsbPortNumber;   // Board type identification via port topology
  *PortMapping = mUsbOcIndex;     // Port-to-OC index mapping table
  *OcConfig    = &mUsbOcConfigData; // Combined OC config including PUSB header
  return EFI_SUCCESS;
}


/**
  Registers the USB OC configuration data with the UBA Configuration
  Database protocol.

  @param[in] UbaConfigDb  Pointer to the UBA_CONFIG_DATABASE_PROTOCOL.

  @retval EFI_SUCCESS  The OC data was registered successfully.
  @retval Others       The registration failed.
**/
EFI_STATUS
RegisterUsbOcData (
  IN UBA_CONFIG_DATABASE_PROTOCOL  *UbaConfigDb
  )
{
  EFI_STATUS  Status;

  //
  // Register the OC configuration data with the UBA protocol.
  // The protocol call uses:
  //   - &mUsbOcConfigGuid  : Parameter GUID identifying the OC data
  //   - &mUsbOcConfigData  : The OC configuration data structure
  //   - sizeof (UINT32) * USB_OC_ENTRY_COUNT : Size of OC data portion
  //
  Status = UbaConfigDb->SetData (
                          UbaConfigDb,
                          &mUsbOcConfigGuid,
                          &mUsbOcConfigData,
                          sizeof (UINT32) * USB_OC_ENTRY_COUNT
                          );
  return Status;
}


/**
  Entry point for USB OC Update DXE driver.

  Initializes global UEFI table pointers, validates the UBA platform,
  and registers USB OC mapping configuration with the UBA database.

  @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_UNSUPPORTED        The UBA config database was not found.
  @return EFI_INVALID_PARAMETER  ImageHandle or SystemTable was NULL.
  @return Others                 An error occurred during initialization.

**/
EFI_STATUS
EFIAPI
UsbOcUpdateEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS                    Status;
  UBA_CONFIG_DATABASE_PROTOCOL  *UbaConfigDb;

  //
  // Validate parameters
  //
  if (ImageHandle == NULL) {
    DEBUG ((EFI_D_ERROR, "gImageHandle != ((void *) 0)\n"));
    ASSERT (ImageHandle != NULL);
    return EFI_INVALID_PARAMETER;
  }
  if (SystemTable == NULL) {
    DEBUG ((EFI_D_ERROR, "gST != ((void *) 0)\n"));
    ASSERT (SystemTable != NULL);
    return EFI_INVALID_PARAMETER;
  }

  //
  // Initialize global table pointers
  //
  gImageHandle    = ImageHandle;
  gSystemTable    = SystemTable;
  gBootServices   = SystemTable->BootServices;
  gRuntimeServices = SystemTable->RuntimeServices;

  if (gBootServices == NULL) {
    DEBUG ((EFI_D_ERROR, "gBS != ((void *) 0)\n"));
    ASSERT (gBootServices != NULL);
    return EFI_INVALID_PARAMETER;
  }
  if (gRuntimeServices == NULL) {
    DEBUG ((EFI_D_ERROR, "gRT != ((void *) 0)\n"));
    ASSERT (gRuntimeServices != NULL);
    return EFI_INVALID_PARAMETER;
  }

  //
  // Initialize HOB subsystem
  //
  GetUbaPlatformHob ();

  //
  // Log the UBA configuration being applied
  //
  DEBUG ((EFI_D_INFO, "UBA:UsbOcUpdate-TypeLightningRidgeEXECB3\n"));

  //
  // Locate the UBA Configuration Database protocol
  //
  UbaConfigDb = GetUbaConfigDb ();
  if (UbaConfigDb == NULL) {
    DEBUG ((EFI_D_ERROR, "Failed to locate UBA Config Database protocol\n"));
    return EFI_UNSUPPORTED;
  }

  //
  // Register the USB OC mapping configuration
  //
  Status = RegisterUsbOcData (UbaConfigDb);
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "Failed to register USB OC config: %r\n", Status));
    return Status;
  }

  DEBUG ((EFI_D_INFO, "USB OC configuration registered successfully for LightningRidge EX EC B3\n"));

  return EFI_SUCCESS;
}