Newer
Older
AMI-Aptio-BIOS-Reversed / UsbOcUpdateDxeNeonCityEPECB / UsbOcUpdateDxeNeonCityEPECB.c
@Ajax Dong Ajax Dong 2 days ago 28 KB Init
/** @file
  UsbOcUpdateDxeNeonCityEPECB -- USB Overcurrent Update for NeonCity EPECB Platform

  This UBA (Universal Board Architecture) DXE driver configures the USB
  Overcurrent (OC) mapping for the NeonCity EPECB platform on Purley
  (Grantley/PurleyRefresh) systems with Lewisburg (LBG) or CDF PCH.

  The driver:
    - Initializes UEFI boot/runtime services and DXE services table
    - Locates the MM PCI Base protocol for PCH configuration space access
    - Determines the PCH stepping (via LPC device VID/DID registers)
    - Selects the appropriate USB OC pin mapping table based on stepping
    - Registers the configuration with the platform via the UBA protocol

  Build path:
    PurleyRpPkg/Uba/UbaMain/Dxe/TypeNeonCityEPECB/UsbOcUpdateDxe

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

  Auto-generated from IDB: UsbOcUpdateDxeNeonCityEPECB.efi.i64
  SHA256: eecd2d931ef1798c5947768c5c98e7d92c02381dd21c4bbf48ce73b416be2462

**/

#include "UsbOcUpdateDxeNeonCityEPECB.h"

//
// ---------------------------------------------------------------------------
// Global variable declarations
// ---------------------------------------------------------------------------
//
#pragma pack(push, 1)

//
// MM PCI Base Protocol interface (used for PCH register access)
//
typedef struct {
  UINT64    Signature;
  //
  // Function 0: Called to perform MM PCI read/write operations.
  // Uses an INPUT/OUTPUT parameter block with:
  //   [0]: 0x00F80000 (PCIEXBAR base register address)
  //   [1]: 0 (reserved)
  //   [2]: 512 (register offset within config space)
  //   [3]: 0 (value)
  //
  UINT64    (*MmPciBaseOp)(UINT32 Param[6]);
} MM_PCI_BASE_PROTOCOL;

//
// UBA Protocol Entry Point structure
//
typedef struct {
  //
  // Function pointer table for platform-specific UBA operations.
  // Typically indexed by offset to dispatch board-specific actions.
  //
  UINT64    (*Dispatch[4])(VOID);
} UBA_PROTOCOL;

#pragma pack(pop)

//
// Global data -- mapped from .data segment
//

//
// Driver global binding handles
//
#pragma section(".data")

//
// gImageHandle - stored copy of the EFI image handle (at 0x14B8)
//
STATIC EFI_HANDLE           mImageHandle;

//
// gST - stored copy of the EFI System Table pointer (at 0x14A8)
//
STATIC EFI_SYSTEM_TABLE     *mSystemTable;

//
// gBS - stored copy of the Boot Services table pointer (at 0x14B0)
//
STATIC EFI_BOOT_SERVICES    *mBootServices;

//
// gRT - stored copy of the Runtime Services table pointer (at 0x14C0)
//
STATIC EFI_RUNTIME_SERVICES *mRuntimeServices;

//
// gDS - Dxe Services Table pointer (at 0x14D0)
//
STATIC VOID                 *mDxeServicesTable;

//
// mPciUsra - MM PCI Base Protocol pointer (at 0x14D8)
//
STATIC MM_PCI_BASE_PROTOCOL *mMmPciBase;

//
// Protocol cache: UEFI Debug Protocol (at 0x14C8)
//
STATIC VOID                 *mDebugProtocol;

//
// mHobList - HOB list pointer (at 0x14E0)
//
STATIC VOID                 *mHobList;

//
// mPcd - PCD Protocol pointer (at 0x14F0)
//
STATIC VOID                 *mPcd;

//
// PCH stepping cache (at 0x14A0)
//
STATIC UINTN                mPchStepping = PCH_STEP_UNSUPPORTED;

//
// PCH stepping override / board type (from CMOS or SMBIOS) (at 0x14F8)
//
STATIC UINT8                mPchSteppingBoardType;

//
// UBA Protocol function table (queried during entry point)
// Stored at 0x14E8
//
STATIC UBA_PROTOCOL         *mUbaProtocol;

//
// ---------------------------------------------------------------------------
// Forward declarations
// ---------------------------------------------------------------------------
//

/**
  Locates and caches the UEFI DebugPort protocol.

  Uses BootServices::LocateProtocol to find the UEFI DebugPort protocol
  (GUID at unk_1340). Also checks the CMOS/RTC to determine the debug
  log level mask.

  @return  Pointer to the DebugPort protocol, or NULL if not found
           or if the UEFI memory map is too large ( > 0x10 entries).

**/
STATIC
VOID *
GetDebugProtocol (
  VOID
  );

/**
  Debug print wrapper that conditionally prints based on log level.

  Checks the current debug level (derived from CMOS register 0x4B)
  and only prints if the requested level @p ErrorLevel matches the
  current board configuration.

  @param[in] ErrorLevel  DEBUG_ERROR (0x80000000) or DEBUG_INFO (0x00000040)
  @param[in] Format      Format string for the debug message.
  @param[in] ...         Variable arguments for the format string.

  @retval TRUE   Message was printed.
  @retval FALSE  Message was suppressed or no DebugPort protocol available.

**/
STATIC
BOOLEAN
DebugPrintWorker (
  IN UINTN        ErrorLevel,
  IN CONST CHAR8  *Format,
  ...
  );

/**
  ASSERT wrapper that reports an assertion failure via DebugPort.

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

**/
STATIC
VOID
AssertWorker (
  IN CONST CHAR8  *FileName,
  IN UINTN        LineNumber,
  IN CONST CHAR8  *Description
  );

/**
  Locates a configuration table in the System Table by GUID.

  Iterates through the System Table's configuration table array
  and returns the first table whose GUID matches.

  @param[in]  TableGuid   Pointer to the GUID to match.
  @param[out] Table       Receives a pointer to the located table.

  @retval EFI_SUCCESS           The table was found.
  @retval EFI_NOT_FOUND         No matching table was found.

**/
STATIC
EFI_STATUS
EfiGetConfigTable (
  IN EFI_GUID   *TableGuid,
  OUT VOID      **Table
  );

/**
  Reads a 16-bit value from a UEFI configuration table using the GUID
  at @p a1. Checks the pointer alignment before dereferencing.

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

  @return   The 64-bit value at the buffer address.

**/
STATIC
UINT64
ReadUnaligned64 (
  IN CONST VOID  *Buffer
  );

/**
  Compares two GUIDs by checking their Data1 and Data2 fields
  via alignment-safe reads.

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

  @retval TRUE   The GUIDs are equal.
  @retval FALSE  The GUIDs are not equal.

**/
STATIC
BOOLEAN
CompareGuid (
  IN EFI_GUID  *Guid1,
  IN EFI_GUID  *Guid2
  );

/**
  Reads a 16-bit value from a memory-mapped IO port (unaligned safe).

  @param[in] Address  The address to read from (must be 16-bit aligned).

  @return   The 16-bit value at the address.

**/
STATIC
UINT16
MmioRead16 (
  IN UINT16  *Address
  );

/**
  Gets the PCH Vendor ID / Device ID from the LPC bridge configuration
  space via MM PCI.

  Reads the LPC bridge device at Bus 0, Device 31, Function 0 and
  returns the combined Vendor ID and Device ID register.

  @return   The PCI VID/DID register value (at offset 0x00).

**/
STATIC
UINT16
PchGetLpcDid (
  VOID
  );

/**
  Gets the PCH stepping type from the combined VID/DID.

  Maps the PCI Device ID of the LPC bridge to an internal stepping
  enumeration used for USB OC table selection.

  @return  PCH stepping identifier (PCH_STEP_* constants).

**/
STATIC
UINTN
PchGetStepping (
  VOID
  );

//
// ---------------------------------------------------------------------------
// Debug Protocol GUID
// ---------------------------------------------------------------------------
//
EFI_GUID gEfiDebugPortProtocolGuid = { 0xEBA4E8D2, 0x3858, 0x41EC, { 0xA2, 0x81, 0x26, 0x47, 0xBA, 0x96, 0x60, 0xD0 } };

//
// ---------------------------------------------------------------------------
// USB OC configuration data structures (referenced via UBA protocol)
// ---------------------------------------------------------------------------
//

//
// USB Overcurrent pin mapping table for NeonCity EPECB (A-step variant).
// Maps each USB port to its OC pin number. Index 0..47.
// (Referenced as unk_13B0 in the IDB)
//
USB_OC_UPDATE_CONFIG mUsbOcUpdateA = {
  {
    0,   // Port  0 -> OC Pin 0
    0,   // Port  1 -> OC Pin 0
    1,   // Port  2 -> OC Pin 1
    1,   // Port  3 -> OC Pin 1
    2,   // Port  4 -> OC Pin 2
    2,   // Port  5 -> OC Pin 2
    3,   // Port  6 -> OC Pin 3
    3,   // Port  7 -> OC Pin 3
    4,   // Port  8 -> OC Pin 4
    4,   // Port  9 -> OC Pin 4
    5,   // Port 10 -> OC Pin 5
    5,   // Port 11 -> OC Pin 5
    6,   // Port 12 -> OC Pin 6
    6,   // Port 13 -> OC Pin 6
    7,   // Port 14 -> OC Pin 7
    7,   // Port 15 -> OC Pin 7
  }
};

//
// USB Overcurrent pin mapping table for NeonCity EPECB (B-step variant).
// (Referenced as unk_1420 in the IDB)
//
USB_OC_UPDATE_CONFIG mUsbOcUpdateB = {
  {
    0,   // Port  0 -> OC Pin 0
    0,   // Port  1 -> OC Pin 0
    1,   // Port  2 -> OC Pin 1
    1,   // Port  3 -> OC Pin 1
    2,   // Port  4 -> OC Pin 2
    2,   // Port  5 -> OC Pin 2
    3,   // Port  6 -> OC Pin 3
    3,   // Port  7 -> OC Pin 3
    4,   // Port  8 -> OC Pin 4
    4,   // Port  9 -> OC Pin 4
    5,   // Port 10 -> OC Pin 5
    5,   // Port 11 -> OC Pin 5
    6,   // Port 12 -> OC Pin 6
    6,   // Port 13 -> OC Pin 6
    7,   // Port 14 -> OC Pin 7
    7,   // Port 15 -> OC Pin 7
  }
};

//
// USB OC update protocol dispatch table.
// (Referenced as unk_13F8 and unk_1460 in the IDB)
//
UINT64 mUsbOcUpdateDispatch[] = {
  //
  // Dispatch function 0: UsbOcGetSteppingMapping
  // Returns the appropriate USB OC config table for the detected PCH stepping.
  // Based on sub_5C8's logic.
  //
  (UINT64)UsbOcGetSteppingMapping,
  //
  // Dispatch function 1: (reserved)
  //
  (UINT64)0,
  //
  // Dispatch function 2: (reserved)
  //
  (UINT64)0,
  //
  // Dispatch function 3: (reserved)
  //
  (UINT64)0,
};

//
// UBA protocol instance for USB OC update.
//
UBA_PROTOCOL mUbaUsbOcInstance = {
  { (UINT64)UsbOcGetSteppingMapping }
};

//
// ---------------------------------------------------------------------------
// Helper function implementations
// ---------------------------------------------------------------------------
//

/**
  Reads a 16-bit value from a pointer with unaligned-safe access.

  Implements an MMIO read of a 16-bit value. If the address alignment
  check fails (odd address), an assertion is triggered via the ASSERT
  framework.

  @param[in] Address  A pointer to the 16-bit value to read.
                      Must be aligned to a 2-byte boundary.

  @return  The 16-bit value at the given address.

**/
STATIC
UINT16
MmioRead16 (
  IN UINT16  *Address
  )
{
  ASSERT ((UINTN)Address % 2 == 0);
  return *Address;
}

/**
  Reads a 64-bit value from an unaligned buffer.

  @param[in] Buffer  Pointer to the buffer to read from (may be unaligned).

  @return  The 64-bit value read from the buffer.

**/
STATIC
UINT64
ReadUnaligned64 (
  IN CONST VOID  *Buffer
  )
{
  ASSERT (Buffer != NULL);
  return *(UINT64 *)Buffer;
}

/**
  Compares two GUID structures by their Data1 and Data2 fields.

  Uses ReadUnaligned64 to safely read the first two QWORDs from each
  GUID and compares them. This avoids alignment issues on platforms
  where GUID structures may not be naturally aligned.

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

  @return  TRUE if both GUIDs are identical, FALSE otherwise.

**/
STATIC
BOOLEAN
CompareGuid (
  IN EFI_GUID  *Guid1,
  IN EFI_GUID  *Guid2
  )
{
  UINT64  Data1;
  UINT64  Data2;

  //
  // Read the GUID data in a single comparison-friendly way.
  // GUID layout: Data1 (4 bytes) + Data2 (2 bytes) + Data3 (2 bytes) + Data4 (8 bytes)
  // We compare the first 8 bytes and the remaining bytes.
  //
  Data1 = ReadUnaligned64 (Guid1);
  Data2 = ReadUnaligned64 (Guid2);

  return Data1 == Data2;
}

/**
  Locates a configuration table in the System Table by matching its GUID.

  Iterates through the configuration table array indexed from the EFI
  System Table. The number of entries is stored at SystemTable + 104
  (NumberOfTableEntries), and the table array is at SystemTable + 112
  (ConfigurationTable).

  @param[in]  TableGuid  Pointer to the GUID to search for.
  @param[out] Table      Receives a pointer to the matching configuration table.

  @retval EFI_SUCCESS           The table was found and returned in @p Table.
  @retval EFI_NOT_FOUND         No configuration table with the given GUID exists.

**/
STATIC
EFI_STATUS
EfiGetConfigTable (
  IN EFI_GUID   *TableGuid,
  OUT VOID      **Table
  )
{
  EFI_STATUS              Status;
  UINTN                   Index;
  EFI_CONFIGURATION_TABLE *ConfigTable;
  UINTN                   TableEntryCount;

  ASSERT (TableGuid != NULL);
  ASSERT (Table != NULL);

  *Table    = NULL;
  Status    = EFI_NOT_FOUND;

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

  if (TableEntryCount == 0) {
    return EFI_NOT_FOUND;
  }

  for (Index = 0; Index < TableEntryCount; Index++) {
    if (CompareGuid (TableGuid, ConfigTable[Index].VendorGuid)) {
      *Table  = ConfigTable[Index].VendorTable;
      Status  = EFI_SUCCESS;
      break;
    }
  }

  return Status;
}

/**
  Locates and caches the DebugPort protocol.

  Uses LocateProtocol through the Boot Services table to find the
  DebugPort protocol (using gEfiDebugPortProtocolGuid). The result
  is cached in a static variable for subsequent calls.

  Checks the UEFI memory map descriptor count - if more than 16
  descriptors are present, the protocol lookup is skipped.

  @return  Pointer to the DebugPort protocol instance.
           NULL if not available.

**/
STATIC
VOID *
GetDebugProtocol (
  VOID
  )
{
  VOID   *Protocol;
  UINTN  MemoryMapSize;
  UINTN  MapKey;
  UINTN  DescriptorSize;
  UINT32 DescriptorVersion;

  Protocol = mDebugProtocol;
  if (Protocol == NULL) {
    //
    // Probe the UEFI memory map size as a sanity check.
    // GetMemoryMap will return the required buffer size.
    //
    MemoryMapSize = 0;
    mBootServices->GetMemoryMap (
                     &MemoryMapSize,
                     NULL,
                     &MapKey,
                     &DescriptorSize,
                     &DescriptorVersion
                     );

    //
    // Only attempt to locate the protocol if the memory map is small
    // (<= 0x10 descriptors). This is a heuristic used by DebugLib.
    //
    if (MemoryMapSize / DescriptorSize <= 0x10) {
      //
      // Locate the DebugPort protocol
      //
      if (EFI_ERROR (mBootServices->LocateProtocol (
                                       &gEfiDebugPortProtocolGuid,
                                       NULL,
                                       &Protocol
                                       ))) {
        Protocol = NULL;
      }
    }

    mDebugProtocol = Protocol;
  }

  return Protocol;
}

/**
  Debug print with CMOS-based log level filtering.

  Reads the board type / log level from CMOS register 0x4B and
  filteres messages based on the requested ErrorLevel.
  Also supports a fallback to memory-mapped I/O at 0xFDAF0490 for
  platforms where CMOS returns 0.

  @param[in] ErrorLevel  The debug message error level.
  @param[in] Format      The format string.
  @param[in] ...         Variable arguments.

  @return  Whether the message was printed.

**/
STATIC
BOOLEAN
DebugPrintWorker (
  IN UINTN        ErrorLevel,
  IN CONST CHAR8  *Format,
  ...
  )
{
  BOOLEAN                    Printed;
  VA_LIST                    VaList;
  DEBUG_PRINT_PROTOCOL       *DebugPrintProtocol;
  UINT8                      BoardType;

  Printed = FALSE;

  DebugPrintProtocol = (DEBUG_PRINT_PROTOCOL *)GetDebugProtocol ();
  if (DebugPrintProtocol != NULL) {
    //
    // Read board type / debug level from CMOS RTC register 0x4B.
    // Bit 7 is the NMI disable bit, bits 6:0 indicate board type.
    //
    BoardType = IoRead8 (0x70);
    IoWrite8 (0x70, (BoardType & 0x80) | 0x4B);
    BoardType = IoRead8 (0x71);

    //
    // If board type > 3, and it is zero, fall back to MMIO read.
    //
    if (BoardType > 3) {
      if (BoardType == 0) {
        BoardType = (MmioRead8 (0xFDAF0490) & 2) | 1;
      }
    }

    //
    // Map BoardType to an EFI debug mask:
    //   BoardType 1 -> EFI_DAGENT_ERROR (bit 30 = 0x40000004)
    //   BoardType 2+ -> EFI_DAGENT_WARN (bit 31 = 0x80000006)
    //   BoardType 0 -> reserved
    //
    if (BoardType >= 1 && BoardType <= 0xFE) {
      if (BoardType == 1) {
        mPchSteppingBoardType = 4;
      } else {
        mPchSteppingBoardType = 4;
      }
    }

    //
    // Determine the log level mask for this board type
    //
    if (mPchSteppingBoardType & 2) {
      //
      // Use a mask based on actual board type:
      // Type 1 boards: 0x40000004, others: 0x80000006
      //
      if ((mPchSteppingBoardType == 1) ? (ErrorLevel & 0x40000004) : (ErrorLevel & 0x80000006)) {
        VA_START (VaList, Format);
        DebugPrintProtocol->DebugPrint (ErrorLevel, Format, VaList);
        VA_END (VaList);
        Printed = TRUE;
      }
    }
  }

  return Printed;
}

/**
  Report an assertion failure via DebugPort.

  Translates an assertion into a formatted message sent through the
  DebugPort protocol.

  @param[in] FileName     The source file name.
  @param[in] LineNumber   The line number of the assertion.
  @param[in] Description  The assertion description.

**/
STATIC
VOID
AssertWorker (
  IN CONST CHAR8  *File,
  IN UINTN        Line,
  IN CONST CHAR8  *Description
  )
{
  DEBUG_PRINT_PROTOCOL  *DebugPrintProtocol;

  DebugPrintProtocol = (DEBUG_PRINT_PROTOCOL *)GetDebugProtocol ();
  if (DebugPrintProtocol != NULL) {
    DebugPrintProtocol->DebugAssert (File, Line, Description);
  }
}

// ---------------------------------------------------------------------------
// Debug macro expansion wrappers
// ---------------------------------------------------------------------------

#define DEBUG_PRINT(Level, ...)  DebugPrintWorker (Level, __VA_ARGS__)
#define ASSERT_REPORT(File, Line, Desc)  AssertWorker (File, Line, Desc)

// ---------------------------------------------------------------------------
// PCH Stepping Detection
// ---------------------------------------------------------------------------

/**
  Performs an MM PCI configuration space read of the LPC bridge
  Vendor ID / Device ID register.

  Uses the MM PCI Base protocol to access the LPC bridge (Bus 0,
  Device 31, Function 0, Register 0). The protocol requires a
  parameter block:
    param[0] = 0x00F80000 (PCIEXBAR address region)
    param[1] = 0
    param[2] = 512 (register offset 0x00 in config space)
    param[3] = 0

  @return  The PCI VID/DID register value (upper 16 bits = DID, lower = VID).

**/
UINT16
PchGetLpcDid (
  VOID
  )
{
  UINT32  Param[6];

  Param[0] = 0x00F80000;   // PCIEXBAR base region
  Param[1] = 0;
  Param[2] = 512;           // Config space offset 0x00
  Param[3] = 0;

  if (mMmPciBase != NULL) {
    return (UINT16)mMmPciBase->MmPciBaseOp (Param);
  }

  return 0;
}

/**
  Determines the PCH stepping from the LPC device DID.

  Maps the LPC bridge Device ID to the internal stepping enumeration:

    DID 0xA14C -> PCH LBG A0 stepping (ID 0)
    DID 0xA14D -> PCH LBG A1 stepping (ID 1)
    DID 0xA150 -> PCH LBG B0 stepping (ID 2)
    DID >= 0xA151 -> Unsupported
    DID <= 0xA13F -> CDF PCH range (checked further)

    For devices with DID between specific ranges:
      DID 0xA14C, 0xA14D, 0xA150 are the main steppings

    Additional USB OC pin assignment steppings for B-step:
      PCH stepping 16+17 -> OC mapping 35, 36 (B0, B1 2-port OC variant)
      PCH stepping 32+33 -> OC mapping 37, 38 (B0, B1 4-port OC variant)
      PCH stepping 48-53 -> CDF steppings (A0-B3)

  @return  PCH stepping identifier as a UINTN.

**/
UINTN
PchGetStepping (
  VOID
  )
{
  UINTN   Stepping;
  UINT16  LpcDid;
  VOID    *PchInfo;
  UINT8   PchRevision;

  Stepping = mPchStepping;
  if (Stepping != PCH_STEP_UNSUPPORTED) {
    return Stepping;
  }

  //
  // Read the PCH info from board-specific data
  //
  PchInfo = PchGetLpcDid ();  // Actually gets the PCH info struct
  PchRevision = ((UINT8 *)PchInfo)[8];  // Stepping byte from PCH info

  //
  // Determine stepping based on Device ID
  //
  LpcDid = *((UINT16 *)PchInfo + 1);  // Device ID from LPC DID register

  if (LpcDid == 0xA14C) {
    //
    // Lewisburg PCH A0 stepping
    //
    switch (PchRevision) {
      case 0x00:
        Stepping = PCH_STEP_LBG_A0;  // 0
        break;
      case 0x10:
        Stepping = PCH_STEP_LBG_A1;  // 1
        break;
      default:
        Stepping = PCH_STEP_LBG_B0;  // 2 (default)
        break;
    }
  } else if (LpcDid < 0xA140) {
    //
    // CDF PCH family (DID <= 0xA13F)
    // Further classified by PCH stepping revision:
    //   rev == 0 -> 48 (CDF A0)
    //   rev == 2 -> 49 (CDF A1)
    //   rev == 3 -> 50 (CDF B0)
    //   rev == 4 -> 51 (CDF B1)
    //   rev == 8 -> 52 (CDF B2)
    //   rev == 9 -> 53 (CDF B3)
    //
    if (PchRevision <= 48 && (LpcDid + 24128) & 0xFF80) {
      Stepping = PCH_STEP_UNSUPPORTED;
    } else {
      //
      // Check board type and PCH rev to determine exact stepping
      //
      if (PchRevision == 4) {
        if (PchRevision < 2) {
          Stepping = PCH_STEP_CDF_A0;  // 48
        } else {
          Stepping = PCH_STEP_CDF_A1;  // 49
        }
      } else if (PchRevision == 8) {
        Stepping = PCH_STEP_CDF_B0;  // 50
      } else if (PchRevision == 9) {
        Stepping = PCH_STEP_CDF_B1;  // 51
      } else if (PchRevision == 16) {
        Stepping = PCH_STEP_CDF_B2;  // 52
      } else if (PchRevision == 17) {
        Stepping = PCH_STEP_CDF_B3;  // 53
      } else {
        Stepping = PCH_STEP_UNSUPPORTED;
      }
    }
  } else {
    //
    // DID >= 0xA151 -> Unsupported
    //
    Stepping = PCH_STEP_UNSUPPORTED;
  }

  mPchStepping = Stepping;
  return Stepping;
}

/**
  Returns the USB OC pin mapping table appropriate for the current
  PCH stepping.

  Called as a dispatch function through the UBA protocol. Selects
  the correct USB OC configuration data table based on the detected
  PCH stepping.

  @param[out] ConfigA  Receives a pointer to the A-table dispatch
                        (or NULL if not applicable).
  @param[out] ConfigB  Receives a pointer to the B-table dispatch
                        (or NULL if not applicable).
  @param[out] SteppingData  Receives a pointer to the stepping-specific
                            USB OC config data table.

  @retval EFI_SUCCESS  The correct table was selected.

**/
EFI_STATUS
UsbOcGetSteppingMapping (
  OUT VOID  **ConfigA,
  OUT VOID  **ConfigB,
  OUT VOID  **SteppingData
  )
{
  UINTN  Stepping;

  *ConfigA      = &mUbaUsbOcInstance;
  *ConfigB      = &mUsbOcUpdateDispatch;

  Stepping      = PchGetStepping ();
  if (Stepping <= 48) {
    *SteppingData = &mUsbOcUpdateA;
  } else {
    *SteppingData = &mUsbOcUpdateB;
  }

  return EFI_SUCCESS;
}

// ---------------------------------------------------------------------------
// Library constructor-style initializers
// ---------------------------------------------------------------------------

/**
  Process Library Constructor for UefiBootServicesTableLib.

  Sets up the global ImageHandle, SystemTable, BootServices, and
  RuntimeServices pointers from the entry point parameters.

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

**/
STATIC
VOID
ProcessLibraryConstructor (
  IN EFI_HANDLE         ImageHandle,
  IN EFI_SYSTEM_TABLE   *SystemTable
  )
{
  //
  // Save global image handle
  //
  mImageHandle = ImageHandle;
  ASSERT (ImageHandle != NULL);

  //
  // Save system table
  //
  mSystemTable = SystemTable;
  ASSERT (SystemTable != NULL);

  //
  // Save boot services pointer
  //
  mBootServices = SystemTable->BootServices;
  ASSERT (mBootServices != NULL);

  //
  // Save runtime services pointer
  //
  mRuntimeServices = SystemTable->RuntimeServices;
  ASSERT (mRuntimeServices != NULL);
}

/**
  Process Library Constructor for DxeServicesTableLib.

  Uses EfiGetConfigTable to locate the DXE Services Table by GUID
  and caches the pointer in mDxeServicesTable.

**/
STATIC
VOID
ProcessDxeServicesTableConstructor (
  VOID
  )
{
  EFI_GUID  gEfiDxeServicesTableGuid  = DXE_SERVICES_TABLE_GUID;
  EFI_STATUS  Status;

  Status = EfiGetConfigTable (&gEfiDxeServicesTableGuid, &mDxeServicesTable);
  if (EFI_ERROR (Status)) {
    DEBUG_PRINT (DEBUG_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status);
    ASSERT_REPORT (
      __FILE__,
      __LINE__,
      "!EFI_ERROR (Status)"
      );
  }

  ASSERT (mDxeServicesTable != NULL);
}

/**
  Process Library Constructor for DxeMmPciBaseLib.

  Locates the MM PCI Base Protocol via LocateProtocol and caches
  the pointer in mMmPciBase.

**/
STATIC
VOID
ProcessMmPciBaseConstructor (
  VOID
  )
{
  EFI_GUID  gEfiMmPciBaseProtocolGuid  = MM_PCI_BASE_PROTOCOL_GUID;
  EFI_STATUS  Status;

  if (mMmPciBase == NULL) {
    Status = mBootServices->LocateProtocol (
                              &gEfiMmPciBaseProtocolGuid,
                              NULL,
                              (VOID **)&mMmPciBase
                              );
    if (EFI_ERROR (Status)) {
      DEBUG_PRINT (DEBUG_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status);
      ASSERT_REPORT (
        __FILE__,
        __LINE__,
        "!EFI_ERROR (Status)"
        );
    }

    ASSERT (mMmPciBase != NULL);
  }
}

/**
  Process Library Constructor for DxeHobLib.

  Locates the HOB List via EfiGetConfigTable and caches the pointer.

**/
STATIC
VOID
ProcessHobLibConstructor (
  VOID
  )
{
  EFI_GUID  gEfiHobListGuid  = HOB_LIST_GUID;
  EFI_STATUS  Status;

  if (mHobList == NULL) {
    Status = EfiGetConfigTable (&gEfiHobListGuid, &mHobList);
    if (EFI_ERROR (Status)) {
      DEBUG_PRINT (DEBUG_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status);
      ASSERT_REPORT (
        __FILE__,
        __LINE__,
        "!EFI_ERROR (Status)"
        );
    }

    ASSERT (mHobList != NULL);
  }
}

/**
  Process Library Constructor for DxePcdLib.

  Locates the PCD protocol via LocateProtocol and caches the pointer.

**/
STATIC
VOID
ProcessPcdLibConstructor (
  VOID
  )
{
  EFI_GUID  gEfiPcdProtocolGuid  = PCD_PROTOCOL_GUID;
  EFI_STATUS  Status;

  if (mPcd == NULL) {
    Status = mBootServices->LocateProtocol (
                              &gEfiPcdProtocolGuid,
                              NULL,
                              (VOID **)&mPcd
                              );
    if (EFI_ERROR (Status)) {
      DEBUG_PRINT (DEBUG_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status);
      ASSERT_REPORT (
        __FILE__,
        __LINE__,
        "!EFI_ERROR (Status)"
        );
    }

    ASSERT (mPcd != NULL);
  }
}

// ---------------------------------------------------------------------------
// Entry Point
// ---------------------------------------------------------------------------

/**
  Driver entry point for UsbOcUpdateDxeNeonCityEPECB.

  Initializes the UEFI library constructors, registers the debug trace,
  locates the UBA protocol, and installs the USB OC configuration via
  the UBA protocol's board-specific dispatch mechanism.

  @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 USB OC configuration was installed.
  @retval EFI_PROTOCOL_ERROR    The UBA protocol could not be located.
  @retval EFI_INVALID_PARAMETER A parameter was invalid.

**/
EFI_STATUS
EFIAPI
UsbOcUpdateInit (
  IN EFI_HANDLE         ImageHandle,
  IN EFI_SYSTEM_TABLE   *SystemTable
  )
{
  EFI_GUID  gUsbOcProtocolGuid = USB_OC_UPDATE_PROTOCOL_GUID;
  UBA_PROTOCOL  *UbaProtocolInterface;
  EFI_STATUS    Status;

  //
  // Initialize library constructors (sets up global service pointers)
  //
  ProcessLibraryConstructor (ImageHandle, SystemTable);
  ProcessDxeServicesTableConstructor ();
  ProcessMmPciBaseConstructor ();
  ProcessHobLibConstructor ();
  ProcessPcdLibConstructor ();

  //
  // Log the board type trace
  //
  DEBUG_PRINT (DEBUG_INFO, "UBA:UsbOcUpdate-TypeNeonCityEPECB\n");

  //
  // Locate the UBA protocol
  //
  Status = mBootServices->LocateProtocol (
                            &gUsbOcProtocolGuid,
                            NULL,
                            (VOID **)&UbaProtocolInterface
                            );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Install the USB OC configuration by calling the UBA protocol's
  // board-specific entry point (offset 16 in the protocol interface).
  // The entry point receives the USB OC config data tables.
  //
  return ((EFI_STATUS (EFIAPI *)(VOID *, VOID *, UINTN))(
           UbaProtocolInterface->Dispatch[0])) (
           &mUbaUsbOcInstance,
           &mUsbOcUpdateDispatch,
           16
           );
}