/** @file
  UsbOcUpdateDxeLightningRidgeEXECB2.c - USB Overcurrent Update for
  LightningRidge EXEC B2 platform.

  This DXE driver is part of the UBA (Unified BIOS Abstraction) framework
  under PurleyRpPkg.  It is a minimal module that:

  1. Caches UEFI boot services and runtime services pointers from the system
     table (standard DXE driver boilerplate).
  2. Retrieves the HOB (Hand-Off Block) list pointer by searching the UEFI
     configuration table for the entry matching gEfiAcpiTableGuid.
  3. Locates the UBA USB Overcurrent Protocol.
  4. Invokes the protocol's SetData method with the platform GUID and ACPI
     table GUID to install USB overcurrent (OC) settings into ACPI tables.

  The module also contains helper functions for debug output through a UBA
  debug print protocol, which uses CMOS register 0x4B to determine the
  verbosity level at boot time.

  Build origin (from PDB path):
    e:\hs\Build\HR6N0XMLK\DEBUG_VS2015\X64\PurleyRpPkg\Uba\UbaMain\Dxe\
    TypeLightningRidgeEXECB2\UsbOcUpdateDxe\UsbOcUpdateDxe\DEBUG\
    UsbOcUpdateDxeLightningRidgeEXECB2.pdb

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

#include "UsbOcUpdateDxeLightningRidgeEXECB2.h"

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

///
/// Cache of the EFI System Table pointer.
/// Address 0xC50 in .data.
///
EFI_SYSTEM_TABLE  *gSystemTable       = NULL;

///
/// Cache of the EFI Boot Services table pointer.
/// Address 0xC58 in .data, populated from gSystemTable->BootServices (offset
/// 0x60 in EFI_SYSTEM_TABLE).
///
EFI_BOOT_SERVICES  *gBootServices     = NULL;

///
/// Cache of the ImageHandle passed to the entry point.
/// Address 0xC60 in .data.
///
EFI_HANDLE  gImageHandle              = NULL;

///
/// Cache of the EFI Runtime Services table pointer.
/// Address 0xC68 in .data, populated from gSystemTable->RuntimeServices
/// (offset 0x58 in EFI_SYSTEM_TABLE).
///
EFI_RUNTIME_SERVICES  *gRuntimeServices = NULL;

///
/// Cached pointer to the UBA Debug Print Protocol interface.
/// Address 0xC70 in .data.  Lazily initialized by GetDebugPrintProtocol().
/// The protocol is located once and cached for all subsequent calls.
///
UBA_DEBUG_PRINT_PROTOCOL  *gDebugPrintProtocol = NULL;

///
/// Cached HOB list pointer.
/// Address 0xC78 in .data.  Populated by GetHobList() which searches
/// the system configuration table for the gEfiAcpiTableGuid entry.
///
VOID  *gHobList = NULL;

///
/// Value read from CMOS register 0x4B, used to determine the debug output
/// verbosity level.
/// Address 0xC80 in .data.
///
UINT8  gCmosDebugLevel = 0;

///
/// GUID definitions used by the driver.
///
static CONST EFI_GUID  mUsbOcProtocolGuid   = UBA_USB_OC_PROTOCOL_GUID;
static CONST EFI_GUID  mPlatformGuid        = LIGHTNING_RIDGE_EXEC_B2_GUID;
static CONST EFI_GUID  mAcpiTableGuid       = EFI_ACPI_TABLE_GUID;
static CONST EFI_GUID  mDebugPrintGuid      = UBA_DEBUG_PRINT_PROTOCOL_GUID;

///
/// Static configuration data tables provided by this driver.
/// These are returned by UsbOcGetConfigTables().
///
static CONST UINT32  mConfigTable1[] = {
  0x00000000, 0x00000001, 0x00000008, 0x00000008,
  0x00000008, 0x00000002, 0x00000001, 0x00000002,
  0x00000008, 0x00000008, 0x00000008, 0x00000004,
  0x00000008, 0x00000004, 0x00000000, 0x00000000,
  0x00000000, 0x00000000, 0x00000001, 0x00000001,
  0x00000002, 0x00000003, 0x00000003, 0x00000008,
  0x00000008, 0x00000008, 0x00000008
};

static CONST UINT32  mConfigTable2[] = {
  0x00020007, 0x00020007, 0x00020007, 0x00020007,
  0x00020007, 0x00020007, 0x00020007, 0x00020007,
  0x00020007, 0x00020007, 0x00020007, 0x00020007,
  0x00020007, 0x00020007, 0x00020007, 0x00020007,
  0x00000000, 0x00000000, 0x00000000, 0x00000000
};

static CONST UINT32  mConfigTable3[] = {
  0x00030003, 0x00000008, 0x00000008, 0x00000008,
  0x00000008, 0x00020007, 0x00020007, 0x00020007,
  0x00020007, 0x00020007
};

// ---------------------------------------------------------------------------
// ReadUnaligned64  (sub_740)
// ---------------------------------------------------------------------------

///
/// Reads a 64-bit value from the given address with NULL pointer validation.
///
/// This function is used as an unaligned memory read helper (generated from
/// BaseLib/Unaligned.c).  If the pointer is NULL, an assertion is triggered
/// via AssertPrint().
///
/// @param[in] Buffer  Pointer to the 64-bit value to read.
///
/// @return The 64-bit value at the given address.
///
UINT64
EFIAPI
ReadUnaligned64(
  IN CONST VOID  *Buffer
  )
{
  //
  // Validate the buffer pointer.  If NULL, assert with the source file
  // location from BaseLib/Unaligned.c (line 192).
  //
  if (Buffer == NULL) {
    AssertPrint(
      "e:\\hs\\MdePkg\\Library\\BaseLib\\Unaligned.c",
      192,
      "Buffer != ((void *) 0)"
      );
  }

  //
  // Dereference the pointer to read the QWORD.  In the original binary this
  // is an unaligned load; on x64 the hardware handles misalignment natively.
  //
  return *(CONST UINT64 *)Buffer;
}

// ---------------------------------------------------------------------------
// CompareGuid  (sub_6D0)
// ---------------------------------------------------------------------------

///
/// Compares two GUIDs for equality by comparing their 64-bit halves.
///
/// Each GUID is read as two 64-bit values (little-endian QWORDs representing
/// [Data1:Data2] and [Data3:Data4:Data5:Data6:Data7:Data8]) using
/// ReadUnaligned64().  If both QWORD pairs match, the GUIDs are considered
/// equal.
///
/// This is NOT the standard EFI_GUID comparison (which would use the full
/// struct comparison), but rather a UBA-specific inline comparison that avoids
/// calling into the full MemCmp.
///
/// @param[in] Guid1  Pointer to the first GUID.
/// @param[in] Guid2  Pointer to the second GUID.
///
/// @return TRUE if the GUIDs are equal, FALSE otherwise.
///
BOOLEAN
EFIAPI
CompareGuid(
  IN CONST EFI_GUID  *Guid1,
  IN CONST EFI_GUID  *Guid2
  )
{
  //
  // Read the first 64-bit half of each GUID and compare.
  // Compare the second 64-bit half of each GUID.
  //
  return ReadUnaligned64(&Guid1->Data1) == ReadUnaligned64(&Guid2->Data1) &&
         ReadUnaligned64(((CONST UINT8 *)Guid1) + 8) == ReadUnaligned64(((CONST UINT8 *)Guid2) + 8);
}

// ---------------------------------------------------------------------------
// GetDebugPrintProtocol  (sub_4B0)
// ---------------------------------------------------------------------------

///
/// Lazily locates and caches the UBA Debug Print Protocol.
///
/// This function is called by DebugPrint() and AssertPrint() to obtain the
/// protocol interface for debug output.  The protocol is located exactly once
/// via gBS->LocateProtocol() with the UBA_DEBUG_PRINT_PROTOCOL_GUID.
///
/// Before accessing the cache flag, the function raises TPL to NOTIFY level
/// (31 = TPL_HIGH_LEVEL) to serialize potential concurrent access, then
/// restores the original TPL.  If the resulting TPL was below 0x10 (indicating
/// a normal execution context), the protocol is looked up.
///
/// If the LocateProtocol call fails (returns EFI_ERROR), the cache remains
/// NULL and all subsequent calls return NULL without attempting to locate
/// again.
///
/// @return Pointer to the UBA_DEBUG_PRINT_PROTOCOL interface, or NULL if the
///         protocol is not installed or could not be located.
///
UBA_DEBUG_PRINT_PROTOCOL *
GetDebugPrintProtocol(
  VOID
  )
{
  //
  // Fast path: check if the protocol has already been located.
  //
  if (gDebugPrintProtocol != NULL) {
    return gDebugPrintProtocol;
  }

  //
  // Raise TPL to TPL_NOTIFY level (0x1F = 31) to synchronize access.
  // gBS->RaiseTPL() is at offset 0x18 in the EFI_BOOT_SERVICES table.
  //
  UINTN  OldTpl;
  OldTpl = gBootServices->RaiseTPL(TPL_NOTIFY);

  //
  // Restore the original TPL immediately.
  // gBS->RestoreTPL() is at offset 0x20 in the EFI_BOOT_SERVICES table.
  //
  gBootServices->RestoreTPL(OldTpl);

  //
  // Check if the TPL value indicates we are in a context where we can safely
  // locate a protocol.  A TPL value <= 0x10 means we are at or below TPL_CALLBACK.
  //
  if (OldTpl <= TPL_CALLBACK) {
    //
    // Locate the UBA Debug Print Protocol.
    // gBS->LocateProtocol() is at offset 0x140 in the EFI_BOOT_SERVICES table.
    //
    EFI_STATUS  Status;
    Status = gBootServices->LocateProtocol(
                              (EFI_GUID *)&mDebugPrintGuid,
                              NULL,                     // Registration (no notify)
                              (VOID **)&gDebugPrintProtocol
                              );

    //
    // If LocateProtocol failed (Status < 0 / high bit set), clear the cache so
    // that we return NULL.  The CMOVS instruction in the original binary
    // conditionally sets gDebugPrintProtocol to NULL on error.
    //
    if (EFI_ERROR(Status)) {
      gDebugPrintProtocol = NULL;
    }
  }

  return gDebugPrintProtocol;
}

// ---------------------------------------------------------------------------
// DebugPrint  (sub_530)
// ---------------------------------------------------------------------------

///
/// Platform-specific debug print with CMOS-based error level filtering.
///
/// Reads CMOS register 0x4B via I/O ports 0x70/0x71 to determine the boot-time
/// debug verbosity level, then filters the requested ErrorLevel against the
/// enabled mask before calling the UBA Debug Print protocol.
///
/// CMOS register 0x4B decoding:
///   Bits [7:2]  - Preserved from the original CMOS index byte.
///   Bits [1:0]  - Debug level indicator:
///     0 -> Read from MMIO at 0xFDAF0490, bit 1 OR'd with 1.
///     1 -> Filter mask = 0x80000004 (DEBUG_ERROR | DEBUG_INFO)
///     >1 -> Filter mask = 0x80000006 (DEBUG_ERROR | DEBUG_WARN | DEBUG_INFO)
///     0, >3 -> Treated as same path as default.
///
/// @param[in] ErrorLevel  Debug error level mask for the message (e.g.,
///                        0x80000000 for DEBUG_ERROR, 0x80000004 for DEBUG_INFO).
/// @param[in] Format      Format string.
/// @param[in] ...          Variable arguments.
///
/// @return Non-zero if the message was printed, 0 if filtered out or the
///         debug protocol was not available.
///
UINT8
DebugPrint(
  IN UINTN       ErrorLevel,
  IN CONST CHAR8 *Format,
  ...
  )
{
  UBA_DEBUG_PRINT_PROTOCOL  *Protocol;
  UINTN                     FilterMask;
  UINT8                     CmosReg4B;
  UINT8                     CmosIndex;

  //
  // Get the debug print protocol (may be NULL if not installed).
  //
  Protocol = GetDebugPrintProtocol();
  if (Protocol == NULL) {
    return 0;
  }

  //
  // Read CMOS register 0x4B to determine the debug verbosity filter.
  //
  // Preserve bit 7 of CMOS index 0x70 (NMI mask).
  //
  CmosIndex = __inbyte(0x70);
  __outbyte(0x70, CmosIndex & 0x80 | 0x4B);
  CmosReg4B = __inbyte(0x71);

  //
  // Decode the CMOS value to select the filter mask.
  // Note: The `n3_1 = n3; if (n3 > 3) n3_1 = ...` structure indicates that
  // values 0 and >3 are treated similarly, while values 1, 2, 3 have distinct
  // handling.
  //
  if (CmosReg4B == 0) {
    //
    // CMOS value is 0: read an MMIO register at 0xFDAF0490, extract bit 1,
    // and OR with 1 to get the effective level.
    //
    CmosReg4B = (*(volatile UINT8 *)0xFDAF0490 & 2) | 1;
  } else if (CmosReg4B > 3) {
    //
    // Values > 3 are clamped to... the original value itself (no-op in the
    // filter logic).
    //
    CmosReg4B = CmosReg4B;
  }

  //
  // Determine the filter mask based on the decoded CMOS level.
  //
  if (CmosReg4B == 1) {
    //
    // Level 1: allow DEBUG_ERROR (0x80000000) and DEBUG_INFO (0x80000004).
    //
    FilterMask = 0x80000004;
  } else {
    //
    // Other levels: allow more verbose output.
    // 0x80000006 = DEBUG_ERROR | DEBUG_WARN | DEBUG_INFO + verbose.
    //
    FilterMask = 0x80000006;
  }

  //
  // Check if the requested ErrorLevel falls within the filter mask.
  //
  if ((FilterMask & ErrorLevel) != 0) {
    VA_LIST  VaList;
    VA_START(VaList, Format);

    //
    // Call the UBA Debug Print protocol's DebugPrint at offset +0.
    //
    Protocol->DebugPrint(ErrorLevel, Format, VaList);
  }

  return (UINT8)(UINTN)Protocol;
}

// ---------------------------------------------------------------------------
// AssertPrint  (sub_5B8)
// ---------------------------------------------------------------------------

///
/// Assertion failure print handler.
///
/// Called when an ASSERT() macro fails in the driver or in library code.
/// Prints the assertion location (file + line) and expression using the
/// UBA Debug Print protocol's assert handler (at offset +8 in the protocol
/// interface).
///
/// @param[in] FileName    Source file name where the assertion occurred.
/// @param[in] LineNumber  Line number of the assertion.
/// @param[in] Description The assertion expression or message.
///
/// @return EFI_SUCCESS if print succeeded, or EFI_NOT_FOUND if the debug
///         protocol was not available.
///
EFI_STATUS
EFIAPI
AssertPrint(
  IN CONST CHAR8  *FileName,
  IN UINTN        LineNumber,
  IN CONST CHAR8  *Description
  )
{
  UBA_DEBUG_PRINT_PROTOCOL  *Protocol;

  //
  // Get the debug print protocol (lazy-init).
  //
  Protocol = GetDebugPrintProtocol();
  if (Protocol == NULL) {
    return EFI_NOT_FOUND;
  }

  //
  // Call the assert handler at offset +8 in the protocol interface.
  //
  if (Protocol->AssertHandler != NULL) {
    ((EFI_STATUS (EFIAPI *)(CONST CHAR8 *, UINTN, CONST CHAR8 *))
     Protocol->AssertHandler)(FileName, LineNumber, Description);
  }

  return EFI_SUCCESS;
}

// ---------------------------------------------------------------------------
// GetHobList  (sub_5F8)
// ---------------------------------------------------------------------------

///
/// Retrieves the HOB (Hand-Off Block) list pointer by searching the UEFI
/// Configuration Table for the entry matching gEfiAcpiTableGuid.
///
/// In the DXE phase, the HOB list pointer is traditionally stored in the
/// system configuration table under gEfiHobListGuid.  On this platform, the
/// same GUID value (0x7739F24C-93D7-11D4-9A3A-0090273FC14D) is used as a
/// configuration table key.
///
/// The function scans gSystemTable->ConfigurationTable[] (located at offset
/// 0x70 in EFI_SYSTEM_TABLE) for an entry whose VendorGuid matches
/// gEfiAcpiTableGuid.  If found, the VendorTable pointer is cached in
/// gHobList and returned.
///
/// If the GUID is not found in the configuration table, a diagnostic
/// ASSERT_EFI_ERROR is logged via DebugPrint.  If the resulting gHobList is
/// still NULL, a second assertion fires.
///
/// @return Pointer to the HOB list, or NULL if not found.
///
VOID *
GetHobList(
  VOID
  )
{
  UINTN                   Index;
  EFI_CONFIGURATION_TABLE *ConfigTable;
  UINTN                   EntryCount;

  //
  // Fast path: return cached value if already initialized.
  //
  if (gHobList != NULL) {
    return gHobList;
  }

  //
  // Initialize cache to NULL.
  //
  gHobList = NULL;

  //
  // Get the configuration table information from the system table.
  // EFI_SYSTEM_TABLE layout (offsets from the table base):
  //   +0x68: NumberOfTableEntries
  //   +0x70: ConfigurationTable pointer
  //
  EntryCount = gSystemTable->NumberOfTableEntries;
  ConfigTable = gSystemTable->ConfigurationTable;

  //
  // Scan the configuration table for an entry whose GUID matches
  // gEfiAcpiTableGuid.
  //
  for (Index = 0; Index < EntryCount; Index++) {
    //
    // Compare GUID via CompareGuid().
    // Each configuration table entry is 0x18 bytes:
    //   +0x00: VendorGuid (EFI_GUID, 16 bytes)
    //   +0x10: VendorTable (VOID*, 8 bytes)
    //
    if (CompareGuid(
          (CONST EFI_GUID *)&ConfigTable[Index].VendorGuid,
          (CONST EFI_GUID *)&mAcpiTableGuid
          )) {
      //
      // Found the entry.  Cache the VendorTable pointer.
      //
      gHobList = ConfigTable[Index].VendorTable;
      break;
    }
  }

  //
  // If the GUID was not found, emit a diagnostic.
  // Note: In EDK2, this corresponds to an ASSERT_EFI_ERROR with status 14
  // (0x800000000000000E = EFI_NOT_FOUND).
  //
  if (gHobList == NULL) {
    DebugPrint(
      0x80000000,                              // DEBUG_ERROR
      "\nASSERT_EFI_ERROR (Status = %r)\n",    // from string at 0x780
      0x800000000000000EULL                    // EFI_NOT_FOUND
      );

    AssertPrint(
      "e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
      54,
      "!EFI_ERROR (Status)"
      );

    if (gHobList == NULL) {
      AssertPrint(
        "e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
        55,
        "mHobList != ((void *) 0)"
        );
    }
  }

  return gHobList;
}

// ---------------------------------------------------------------------------
// UsbOcGetConfigTables  (sub_48C)
// ---------------------------------------------------------------------------

///
/// Returns pointers to the platform-specific static data tables.
///
/// This function is referenced by the component name / configuration tables
/// that are installed alongside the driver.  It provides three static data
/// arrays that contain platform-specific configuration for the USB OC
/// settings on LightningRidge EXEC B2.
///
/// The function is NOT called from the main entry path; it exists as a
/// callback accessible through the driver's static data tables.
///
/// @param[out] ConfigData1  Returns a pointer to the first configuration
///                          table (mConfigTable1, at 0xBB0).
/// @param[out] ConfigData2  Returns a pointer to the second configuration
///                          table (mConfigTable2, at 0xBF0).
/// @param[out] ConfigData3  Returns a pointer to the third configuration
///                          table (mConfigTable3, at 0xC18).
///
/// @return Always returns 0 (EFI_SUCCESS).
///
UINTN
EFIAPI
UsbOcGetConfigTables(
  OUT VOID  **ConfigData1,
  OUT VOID  **ConfigData2,
  OUT VOID  **ConfigData3
  )
{
  //
  // Return pointers to the static configuration data arrays.
  //
  *ConfigData1 = (VOID *)&mConfigTable1;
  *ConfigData2 = (VOID *)&mConfigTable2;
  *ConfigData3 = (VOID *)&mConfigTable3;

  return 0;
}

// ---------------------------------------------------------------------------
// UsbOcUpdateEntryPoint  (_ModuleEntryPoint / 0x390)
// ---------------------------------------------------------------------------

///
/// Main entry point for the UsbOcUpdateDxeLightningRidgeEXECB2 driver.
///
/// Performs the following operations in order:
///
/// 1. Caches the UEFI service table pointers (gImageHandle, gSystemTable,
///    gBootServices, gRuntimeServices) with NULL-pointer validation via
///    ASSERT.  If any pointer is NULL, AssertPrint() is called with the
///    appropriate file/line from the EDK2 library sources.
///
/// 2. Calls GetHobList() to locate the HOB list pointer from the system
///    configuration table.  This is required for the UBA framework.
///
/// 3. Calls DebugPrint() with DEBUG_ERROR level to log the driver's
///    platform identifier string:
///    "UBA:UsbOcUpdate-TypeLightningRidgeEXECB2\n"
///
/// 4. Locates the UBA USB Overcurrent Protocol via
///    gBS->LocateProtocol(mUsbOcProtocolGuid, NULL, &Interface).
///
/// 5. If found, invokes the protocol's SetData method (at offset +0x10)
///    with:
///    - The platform GUID (mPlatformGuid = LightningRidgeEXECB2)
///    - The ACPI table GUID (mAcpiTableGuid) as the destination
///    - DataSize = 0x10 (16 bytes, size of a GUID)
///
/// @param[in] ImageHandle  The firmware-allocated handle for this driver.
/// @param[in] SystemTable  Pointer to the EFI System Table.
///
/// @return EFI_STATUS from the USB OC protocol's SetData method, or the
///         status from LocateProtocol if the protocol was not found.
///
EFI_STATUS
EFIAPI
UsbOcUpdateEntryPoint(
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS            Status;
  UBA_USB_OC_PROTOCOL   *UsbOcProtocol;
  VOID                  *HobList;

  // -----------------------------------------------------------------------
  // Step 1: Cache UEFI service table pointers
  // -----------------------------------------------------------------------

  //
  // Save the ImageHandle (address 0xC60).
  //
  gImageHandle = ImageHandle;
  if (ImageHandle == NULL) {
    //
    // Assert: gImageHandle != NULL (from BootServicesTableLib.c at line 51).
    //
    AssertPrint(
      "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
      51,
      "gImageHandle != ((void *) 0)"
      );
  }

  //
  // Save the SystemTable pointer (address 0xC50).
  //
  gSystemTable = SystemTable;
  if (SystemTable == NULL) {
    //
    // Assert: gST != NULL (from BootServicesTableLib.c at line 57).
    //
    AssertPrint(
      "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
      57,
      "gST != ((void *) 0)"
      );
  }

  //
  // Save BootServices pointer from SystemTable->BootServices (offset 0x60
  // in EFI_SYSTEM_TABLE).  Address 0xC58.
  //
  gBootServices = SystemTable->BootServices;
  if (gBootServices == NULL) {
    //
    // Assert: gBS != NULL (from BootServicesTableLib.c at line 63).
    //
    AssertPrint(
      "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
      63,
      "gBS != ((void *) 0)"
      );
  }

  //
  // Save RuntimeServices pointer from SystemTable->RuntimeServices (offset
  // 0x58 in EFI_SYSTEM_TABLE).  Address 0xC68.
  //
  gRuntimeServices = SystemTable->RuntimeServices;
  if (gRuntimeServices == NULL) {
    //
    // Assert: gRT != NULL (from RuntimeServicesTableLib.c at line 47).
    //
    AssertPrint(
      "e:\\hs\\MdePkg\\Library\\UefiRuntimeServicesTableLib\\UefiRuntimeServicesTableLib.c",
      47,
      "gRT != ((void *) 0)"
      );
  }

  // -----------------------------------------------------------------------
  // Step 2: Retrieve the HOB list
  // -----------------------------------------------------------------------

  //
  // GetHobList searches the system configuration table for the entry matching
  // gEfiAcpiTableGuid.  This initializes gHobList for use by the UBA framework.
  //
  HobList = GetHobList();

  // -----------------------------------------------------------------------
  // Step 3: Log the platform identifier
  // -----------------------------------------------------------------------

  //
  // Print the UBA driver identifier string for debugging/auditing purposes.
  //
  DebugPrint(
    0x80000000,                                    // DEBUG_ERROR level
    "UBA:UsbOcUpdate-TypeLightningRidgeEXECB2\n"   // from string at 0x7C0
    );

  // -----------------------------------------------------------------------
  // Step 4: Locate the UBA USB Overcurrent Protocol
  // -----------------------------------------------------------------------

  //
  // Locate the protocol interface by GUID.
  //
  UsbOcProtocol = NULL;
  Status = gBootServices->LocateProtocol(
                            (EFI_GUID *)&mUsbOcProtocolGuid,
                            NULL,
                            (VOID **)&UsbOcProtocol
                            );

  //
  // Check if the protocol was found.
  //
  if (!EFI_ERROR(Status) && UsbOcProtocol != NULL) {

    // ---------------------------------------------------------------------
    // Step 5: Call the protocol method to install USB OC settings
    // ---------------------------------------------------------------------

    //
    // Invoke the SetData method at offset +0x10 in the UBA USB OC protocol.
    //
    // Parameters:
    //   RCX = UsbOcProtocol (This pointer)
    //   RDX = &mPlatformGuid (GUID for LightningRidge EXEC B2)
    //   R8  = &mAcpiTableGuid (destination ACPI table GUID)
    //   R9  = 0x10 (DataSize = size of GUID = 16 bytes)
    //
    Status = UsbOcProtocol->SetData(
                              UsbOcProtocol,
                              (EFI_GUID *)&mPlatformGuid,
                              (EFI_GUID *)&mAcpiTableGuid,
                              0x10
                              );
  }

  //
  // Return the status from either LocateProtocol or SetData.
  //
  return Status;
}