/** @file
  UsbOcUpdateDxeLightningRidgeEXECB4 - USB Overcurrent Mapping Driver

  DXE driver that installs a USB Overcurrent (UsbOc) configuration protocol
  for the LightningRidge EX EC B4 platform. This driver is part of the UBA
  (Universal Board Architecture) framework.

  Functional flow:
  1. _ModuleEntryPoint initializes gST, gBS, gRT, gImageHandle globals.
  2. Calls GetHobList() to retrieve the HOB list from the system table.
  3. Calls DebugPrintEx() to log "UBA:UsbOcUpdate-TypeLightningRidgeEXECB4\n".
  4. Calls gBS->AllocatePool() + gBS->InstallProtocolInterface() to publish
     the UBA USB OC protocol with platform-specific port mappings.

  The port mapping is selected at runtime by reading CMOS index 0x4B
  (board revision strap). Three tables are compiled in:
    - mUsbOcTableDefault (0xba0): Default mapping
    - mUsbOcTableAlt (0xbd0): Alternate/B2 stepping mapping
    - mUsbOcTableExt (0xc10): Extended mapping (all OC pin 0x0702)

  Build path:
    e:\hs\Build\HR6N0XMLK\DEBUG_VS2015\X64\PurleyRpPkg\Uba\UbaMain\Dxe\
    TypeLightningRidgeEXECB4\UsbOcUpdateDxe\UsbOcUpdateDxe\DEBUG\
    UsbOcUpdateDxeLightningRidgeEXECB4.pdb

  Copyright (C) 2025 American Megatrends Inc. (AMI)
  SPDX-License-Identifier: BSD-2-Clause-Patent

  @par Module Index:
    HR650X BIOS index 0044, SHA256 962bb14e57e35431057e4f23d4e3aae9e698b16facd4f1804c099b46c5a5e605
**/

#include "UsbOcUpdateDxeLightningRidgeEXECB4.h"

//
// ---- UEFI Global Variables ----
//

EFI_BOOT_SERVICES       *gBS          = NULL;
EFI_RUNTIME_SERVICES    *gRT          = NULL;
EFI_SYSTEM_TABLE        *gST          = NULL;
EFI_HANDLE               gImageHandle = NULL;
VOID                    *mHobList     = NULL;
UBA_PROTOCOL_FUNCS      *mUbaProtocol = NULL;

//
// ---- USB OC Configuration Tables ----
//
// These tables are embedded in the .data section at the addresses
// returned by GetUsbOcConfigTables(). The values map USB physical
// ports to overcurrent sense pins.
//
// .data layout:
//   0xba0 (mUsbOcTableDefault): {0,0,0,0, 1,1,1,1, 2,2,2,2}
//   0xbd0 (mUsbOcTableAlt):     {0,0,0,0, 0,0,1,1, 1,1,2,2}
//   0xc10 (mUsbOcTableExt):     all 0x0702
//

USB_OC_CONFIG_TABLE  mUsbOcTableDefault = {
  { 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2 }
};

USB_OC_CONFIG_TABLE  mUsbOcTableAlt = {
  { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2 }
};

USB_OC_CONFIG_TABLE  mUsbOcTableExt = {
  { 0x0702, 0x0702, 0x0702, 0x0702, 0x0702, 0x0702,
    0x0702, 0x0702, 0x0702, 0x0702, 0x0702, 0x0702 }
};

//
// ---- UBA Protocol GUID (binary layout) ----
//
// EFI_GUID {2638009e-3850-4e4b-b05d-042a32dbb9d1}
//
STATIC CONST EFI_GUID  mUbaProtocolGuid = UBA_USBOC_PROTOCOL_GUID;

//
// ---- gEfiHobListGUID (binary layout) ----
//
STATIC CONST EFI_GUID  mEfiHobListGuid = EFI_HOB_LIST_GUID;

//
// ---- USB OC HOB GUID (binary layout) ----
//
STATIC CONST EFI_GUID  mUsbOcHobGuid = USBOC_HOB_GUID;

// ============================================================================
// GetUsbOcConfigTables
// ============================================================================

/**
  Returns pointers to the three board-specific USB OC configuration tables.

  @param[out]  Table2   Pointer to mUsbOcTableAlt  (0xbd0).
  @param[out]  Table1   Pointer to mUsbOcTableDefault (0xba0).
  @param[out]  Table3   Pointer to mUsbOcTableExt   (0xc10).

  @retval EFI_SUCCESS  Always succeeds.
**/
EFI_STATUS
EFIAPI
GetUsbOcConfigTables (
  OUT VOID  **Table2,
  OUT VOID  **Table1,
  OUT VOID  **Table3
  )
{
  *Table2 = &mUsbOcTableAlt;
  *Table1 = &mUsbOcTableDefault;
  *Table3 = &mUsbOcTableExt;
  return EFI_SUCCESS;
}

// ============================================================================
// LocateUbaProtocol
// ============================================================================

/**
  Lazily locate and cache the UBA USB OC protocol.

  Allocates a pool of at least 16 bytes via gBS->AllocatePool (type 31,
  i.e., EfiBootServicesData), then calls gBS->LocateProtocol to find
  the UBA protocol interface. The result is cached in mUbaProtocol.

  @return  Pointer to the located UBA_PROTOCOL_FUNCS, or NULL.
**/
UBA_PROTOCOL_FUNCS *
EFIAPI
LocateUbaProtocol (
  VOID
  )
{
  EFI_STATUS          Status;
  UBA_PROTOCOL_FUNCS  *Protocol;

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

  //
  // Allocate pool for the protocol structure (minimum 16 bytes).
  //
  Status = gBS->AllocatePool (EfiBootServicesData, 16, (VOID **)&Protocol);
  if (EFI_ERROR (Status)) {
    return NULL;
  }
  ZeroMem (Protocol, 16);

  //
  // Locate the UBA USB OC protocol by GUID.
  //
  Status = gBS->LocateProtocol (
                  (EFI_GUID *)&mUbaProtocolGuid,
                  NULL,
                  (VOID **)Protocol
                  );
  if (EFI_ERROR (Status)) {
    gBS->FreePool (Protocol);
    return NULL;
  }

  //
  // Cache the protocol pointer.
  //
  mUbaProtocol = Protocol;

  return Protocol;
}

// ============================================================================
// DebugPrintEx
// ============================================================================

/**
  Board-revision-aware debug print.

  Reads CMOS index 0x4B to determine the board revision/stepping and
  applies a filter mask:
    - If board rev == 1: allow DEBUG_INIT | DEBUG_ERROR (0x80000004)
    - If board rev >= 2: allow DEBUG_INIT | DEBUG_ERROR | DEBUG_INFO (0x80000006)
    - If board rev == 0 and MMIO strap: derive revision from hardware

  @param[in]  DebugMask  Debug message mask.
  @param[in]  Format     Print format string.
  @param[in]  ...        Variable arguments.

  @return  0 if filtered out, otherwise the return from Print function.
**/
UINT8
EFIAPI
DebugPrintEx (
  IN UINT64  DebugMask,
  IN CHAR8   *Format,
  ...
  )
{
  UBA_PROTOCOL_FUNCS  *Protocol;
  UINT64              FilterMask;
  UINT8               BoardRev;
  UINT8               Result;
  VA_LIST             Args;

  Protocol = LocateUbaProtocol ();
  if (Protocol == NULL) {
    return 0;
  }

  //
  // Read board revision from RTC CMOS index 0x4B.
  // Preserve NMI mask (bit 7) in the index register.
  //
  IoWrite8 (RTC_INDEX_PORT, IoRead8 (RTC_INDEX_PORT) & 0x80 | CMOS_BOARD_REV_INDEX);
  BoardRev = IoRead8 (RTC_DATA_PORT);

  //
  // If the raw CMOS value is out of range, try to refine it.
  //
  if ((UINT8)BoardRev > BOARD_REV_MAX) {
    //
    // Board rev appears invalid. If the raw value is 0, check the
    // hardware strap at the MMIO address.
    //
    if (BoardRev == 0) {
      BoardRev = (MmioRead8 (BOARD_STRAP_MMIO) & 2) | 1;
    }
  }

  //
  // Determine the filter mask.
  //
  if ((UINT8)(BoardRev - 1) <= 0xFD) {  // BoardRev >= 1
    FilterMask = DEBUG_MASK_ALL;         // 0x80000006
    if (BoardRev == 1) {
      FilterMask = DEBUG_MASK_INIT | DEBUG_MASK_ERROR;  // 0x80000004
    }
  } else {
    FilterMask = 0;
  }

  //
  // Check if our debug mask passes the filter.
  //
  if ((FilterMask & DebugMask) == 0) {
    return 0;
  }

  VA_START (Args, Format);
  Result = (UINT8)Protocol->Print (DebugMask, Format, Args);
  VA_END (Args);

  return Result;
}

// ============================================================================
// ReportAssertion
// ============================================================================

/**
  Report an assertion/error condition through the UBA protocol.

  Lazily locates the UBA protocol if not yet cached, then calls
  offset 8 (ReportError function) with FileName, LineNumber, and Message.

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

  @return  The return value from the UBA protocol's ReportError function,
           or 0 if the protocol cannot be located.
**/
__int64
EFIAPI
ReportAssertion (
  IN __int64  FileName,
  IN __int64  LineNumber,
  IN __int64  Message
  )
{
  UBA_PROTOCOL_FUNCS  *Protocol;

  Protocol = mUbaProtocol;
  if (Protocol == NULL) {
    Protocol = LocateUbaProtocol ();
  }

  if (Protocol != NULL) {
    return Protocol->ReportError (FileName, LineNumber, Message);
  }

  return 0;
}

// ============================================================================
// ReadUnaligned64Ex
// ============================================================================

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

  Asserts if Buffer is NULL.

  @param[in]  Buffer  Pointer to source memory.

  @return  The 64-bit value at Buffer.
**/
UINT64
EFIAPI
ReadUnaligned64Ex (
  IN CONST VOID  *Buffer
  )
{
  if (Buffer == NULL) {
    ReportAssertion (
      (__int64)"e:\\hs\\MdePkg\\Library\\BaseLib\\Unaligned.c",
      192,
      (__int64)"Buffer != ((void *) 0)"
      );
  }

  return ReadUnaligned64 (Buffer);
}

// ============================================================================
// CompareGuidEx
// ============================================================================

/**
  Compare two GUIDs for equality by reading them as two 64-bit values.

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

  @retval TRUE   GUIDs are identical.
  @retval FALSE  GUIDs differ.
**/
BOOLEAN
EFIAPI
CompareGuidEx (
  IN EFI_GUID  *Guid1,
  IN EFI_GUID  *Guid2
  )
{
  return ReadUnaligned64Ex (Guid1) == ReadUnaligned64Ex (Guid2) &&
         ReadUnaligned64Ex ((UINT8 *)Guid1 + 8) == ReadUnaligned64Ex ((UINT8 *)Guid2 + 8);
}

// ============================================================================
// GetHobList
// ============================================================================

/**
  Locate the HOB list pointer from the UEFI System Table.

  Scans the system table's configuration table array (at SystemTable + 112)
  for the gEfiHobListGuid entry. Once found, the associated pointer is
  cached in mHobList.

  If the HOB list is not found, an assertion is triggered.

  @param[in]  SystemTable  Pointer to the EFI System Table.

  @return  Pointer to the HOB list, or NULL.
**/
VOID *
EFIAPI
GetHobList (
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  UINTN   TableCount;
  UINTN   Index;
  VOID    *HobList;

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

  mHobList = NULL;
  HobList  = NULL;

  //
  // SystemTable->NumberOfTableEntries is at offset 104 in the
  // EFI_SYSTEM_TABLE structure (field after RuntimeServices).
  //
  TableCount = *(UINTN *)((UINT8 *)SystemTable + 104);
  if (TableCount == 0) {
    //
    // No configuration tables -- log assertion.
    //
    DebugPrintEx (DEBUG_MASK_INIT, "\nASSERT_EFI_ERROR (Status = %r)\n", 0x800000000000000ELL);
    ReportAssertion (
      (__int64)"e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
      54,
      (__int64)"!EFI_ERROR (Status)"
      );
    return NULL;
  }

  //
  // SystemTable->ConfigurationTable is at offset 112.
  // Each entry is 24 bytes: { EFI_GUID Guid; VOID *Table; }.
  //
  for (Index = 0; Index < TableCount; Index++) {
    //
    // Compare the table's GUID with gEfiHobListGuid.
    //
    if (CompareGuidEx (
          (EFI_GUID *)((UINT8 *)SystemTable + 112 + Index * 24),
          (EFI_GUID *)&mEfiHobListGuid
          )) {
      //
      // Found the HOB list entry. The table pointer is at offset 16
      // within each configuration table entry.
      //
      HobList = *(VOID **)((UINT8 *)SystemTable + 112 + Index * 24 + 16);
      break;
    }
  }

  if (HobList == NULL) {
    //
    // HOB list not found -- this is an error condition.
    //
    DebugPrintEx (DEBUG_MASK_INIT, "\nASSERT_EFI_ERROR (Status = %r)\n", 0x800000000000000ELL);
    ReportAssertion (
      (__int64)"e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
      54,
      (__int64)"!EFI_ERROR (Status)"
      );
  }

  //
  // If the HOB list was found, verify it is non-NULL.
  //
  if (HobList == NULL) {
    ReportAssertion (
      (__int64)"e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
      55,
      (__int64)"mHobList != ((void *) 0)"
      );
  }

  mHobList = HobList;
  return HobList;
}

// ============================================================================
// _ModuleEntryPoint
// ============================================================================

/**
  Main entry point for the UsbOcUpdateDxeLightningRidgeEXECB4 driver.

  Initializes the UEFI protocol globals and installs the UBA USB OC
  configuration protocol.

  Flow:
    1. Save gImageHandle, gST, gBS, gRT.
    2. Locate the HOB list via GetHobList().
    3. Log the board type via DebugPrintEx().
    4. Allocate pool for the protocol structure.
    5. Install the protocol interface via gBS->InstallProtocolInterface().

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

  @retval EFI_SUCCESS           The protocol was installed successfully.
  @retval EFI_OUT_OF_RESOURCES  Failed to allocate pool.
  @retval Others                Error from InstallProtocolInterface.
**/
EFI_STATUS
EFIAPI
_ModuleEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;

  //
  // 1. Initialize UEFI globals.
  //
  gImageHandle = ImageHandle;
  if (ImageHandle == NULL) {
    ReportAssertion (
      (__int64)"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
      51,
      (__int64)"gImageHandle != ((void *) 0)"
      );
  }

  gST = SystemTable;
  if (SystemTable == NULL) {
    ReportAssertion (
      (__int64)"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
      57,
      (__int64)"gST != ((void *) 0)"
      );
  }

  gBS = SystemTable->BootServices;
  if (gBS == NULL) {
    ReportAssertion (
      (__int64)"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
      63,
      (__int64)"gBS != ((void *) 0)"
      );
  }

  gRT = SystemTable->RuntimeServices;
  if (gRT == NULL) {
    ReportAssertion (
      (__int64)"e:\\hs\\MdePkg\\Library\\UefiRuntimeServicesTableLib\\UefiRuntimeServicesTableLib.c",
      47,
      (__int64)"gRT != ((void *) 0)"
      );
  }

  //
  // 2. Locate the HOB list from the system table.
  //
  GetHobList (SystemTable);

  //
  // 3. Log board type identification string.
  //
  DebugPrintEx (
    DEBUG_MASK_INFO,
    "UBA:UsbOcUpdate-TypeLightningRidgeEXECB4\n"
    );

  //
  // 4. Allocate pool for the UBA USB OC protocol structure.
  //
  {
    UINT32  *ProtocolBuffer;

    Status = gBS->AllocatePool (
                    EfiBootServicesData,
                    16,   // Minimum protocol structure size
                    (VOID **)&ProtocolBuffer
                    );
    if (EFI_ERROR (Status)) {
      return Status;
    }

    ZeroMem (ProtocolBuffer, 16);

    //
    // 5. Install the UBA USB OC protocol.
    //
    // The protocol buffer is installed with the board-specific GUID.
    // The consumer (USB stack) will use this protocol to determine
    // USB port-to-overcurrent-pin mappings.
    //
    Status = gBS->InstallProtocolInterface (
                    &ImageHandle,
                    (EFI_GUID *)&mUbaProtocolGuid,
                    EFI_NATIVE_INTERFACE,
                    ProtocolBuffer
                    );
  }

  return Status;
}