Newer
Older
AMI-Aptio-BIOS-Reversed / SlotDataUpdateDxeLightningRidgeEXECB1 / SlotDataUpdateDxeLightningRidgeEXECB1.c
@Ajax Dong Ajax Dong 2 days ago 26 KB Init
/**
 * @file SlotDataUpdateDxeLightningRidgeEXECB1.c
 *
 * @brief UEFI DXE driver implementation for LightningRidge EXEC B1 slot data update.
 *
 * This driver is part of the AMI UBA (Universal BIOS Architecture) framework.
 * During the DXE phase, it locates the UBA Config protocol and reads PSLT
 * (Platform Slot Table) configuration data that defines PCIe slot properties
 * for the LightningRidge EXEC B1 platform.
 *
 * Build source path: PurleyRpPkg/Uba/UbaMain/Dxe/TypeLightningRidgeEXECB1/
 *                    SlotDataUpdateDxe/SlotDataUpdateDxe/
 * PDB: SlotDataUpdateDxeLightningRidgeEXECB1.pdb
 * Build: VS2015, DEBUG, X64 (PE32+, IMAGE_FILE_MACHINE_AMD64)
 *
 * CRITICAL NOTE: Unlike the EXRP variant which uses UbaProtocol->SetData to
 * install slot configuration, the EXEC B1 variant uses UbaProtocol->GetData
 * to read EXISTING configuration. This reflects a different architectural
 * role -- the EXEC B1 platform has slot configuration pre-prepared by another
 * driver and this driver simply retrieves and validates it.
 *
 * FUNCTION ADDRESS MAP (from original PE binary):
 *   0x390  _ModuleEntryPoint       -- DXE driver entry point
 *   0x4B0  GetSlotCount            -- returns 2 (slot count)
 *   0x4B4  GetSlotData             -- identity pass-through
 *   0x4B8  UbaGetProtocolInterface -- lazy UBA protocol resolve
 *   0x538  UbaDebugPrint           -- CMOS-filtered debug print
 *   0x5C0  UbaDebugAssert          -- ASSERT via UBA protocol
 *   0x600  GetHobList              -- HOB list from config table
 *   0x6D8  IsHobGuidMatch          -- 64-bit GUID compare
 *   0x748  ReadUnaligned64         -- unaligned read w/ NULL check
 *
 * GLOBAL VARIABLE MAP (.data section):
 *   0xBB0  mSlotDataBuffer1        -- PSLT output buffer 1 (32 bytes)
 *   0xBE0  mSlotDataBuffer2        -- PSLT output buffer 2 (40 bytes)
 *   0xC08  gSystemTable            -- EFI_SYSTEM_TABLE*
 *   0xC10  gBootServices           -- EFI_BOOT_SERVICES*
 *   0xC18  gImageHandle            -- EFI_HANDLE
 *   0xC20  gRuntimeServices        -- EFI_RUNTIME_SERVICES*
 *   0xC28  mUbaDebugProtocol            -- UBA protocol interface
 *   0xC30  mHobList                -- HOB list pointer
 *   0xC38  mPlatformType           -- cached platform type byte
 */

#include "SlotDataUpdateDxeLightningRidgeEXECB1.h"

// =============================================================================
// GUID Definitions (.data at 0xB60-0xBAF)
// =============================================================================

/**
 * UBA DEBUG protocol GUID. Address: 0xB60.
 * GUID: 36232936-0E76-31C8-A13A-3AF2FC1C3932
 *
 * Used by UbaGetProtocolInterface() to locate DebugPrint/DebugAssert services.
 * This is a SEPARATE protocol from the UBA Config protocol (at 0xB70).
 */
STATIC EFI_GUID mUbaDebugProtocolGuid = UBA_DEBUG_PROTOCOL_GUID;

/**
 * UBA CONFIG protocol GUID. Address: 0xB70.
 * GUID: E03E0D46-5263-4845-B0A4-58D57B3177E2
 *
 * Used directly in _ModuleEntryPoint to locate the UBA configuration protocol
 * which provides GetData for PSLT reads. This is SEPARATE from the debug
 * protocol used by UbaGetProtocolInterface().
 */
STATIC EFI_GUID mUbaConfigProtocolGuid = UBA_CONFIG_PROTOCOL_GUID;

/**
 * UBA Slot Data PSLT Entry 1 GUID. Address: 0xB90.
 * GUID: B93613E1-48F0-4B32-B3A8-4FEDFC7C1365
 */
STATIC EFI_GUID mUbaSlotDataPsl1Guid = UBA_SLOT_DATA_PSLT1_GUID;

/**
 * UBA Slot Config PSLT Entry 2 GUID. Address: 0xB80.
 * GUID: 226763AE-972C-4E3C-80D1-73B25E8CBBA3
 */
STATIC EFI_GUID mUbaSlotConfigGuid = UBA_SLOT_CONFIG_GUID;

/**
 * DXE Services Table GUID (gEfiDxeServicesTableGuid). Address: 0xBA0.
 * GUID: 7739F24C-93D7-11D4-9A3A-0090273FC14D
 */
STATIC EFI_GUID mEfiDxeServicesTableGuid = DXE_SERVICES_TABLE_GUID;

// =============================================================================
// GUID Externs (alias of STATIC definitions for any external consumers)
// =============================================================================

/** @brief extern alias for mUbaDebugProtocolGuid */
EFI_GUID gUbaDebugProtocolGuid     __attribute__((used)) = UBA_DEBUG_PROTOCOL_GUID;

/** @brief extern alias for mUbaConfigProtocolGuid */
EFI_GUID gUbaConfigProtocolGuid    __attribute__((used)) = UBA_CONFIG_PROTOCOL_GUID;

/** @brief extern alias for mUbaSlotDataPsl1Guid */
EFI_GUID gUbaSlotDataPsl1Guid      __attribute__((used)) = UBA_SLOT_DATA_PSLT1_GUID;

/** @brief extern alias for mUbaSlotConfigGuid */
EFI_GUID gUbaSlotConfigGuid        __attribute__((used)) = UBA_SLOT_CONFIG_GUID;

/** @brief extern alias for mEfiDxeServicesTableGuid */
EFI_GUID gEfiDxeServicesTableGuid  __attribute__((used)) = DXE_SERVICES_TABLE_GUID;

// =============================================================================
// Global Variables (.data at 0xC08-0xC28)
// =============================================================================

/** @brief Saved ImageHandle from entry point. Address: 0xC18. */
EFI_HANDLE gImageHandle = NULL;

/** @brief Saved SystemTable entry point. Address: 0xC08. */
EFI_SYSTEM_TABLE *gSystemTable = NULL;

/** @brief Saved BootServices (SystemTable->BootServices). Address: 0xC10. */
EFI_BOOT_SERVICES *gBootServices = NULL;

/** @brief Saved RuntimeServices (SystemTable->RuntimeServices). Address: 0xC20. */
EFI_RUNTIME_SERVICES *gRuntimeServices = NULL;

// =============================================================================
// Module-Level Static Variables (BSS region, zero-initialized)
// =============================================================================

/** @brief Cached UBA protocol interface. Address: 0xC28. Init: NULL. */
STATIC UBA_DEBUG_PROTOCOL_INTERFACE *mUbaDebugProtocol = NULL;

/** @brief Cached HOB list pointer. Address: 0xC30. Init: NULL. */
STATIC VOID *mHobList = NULL;

/** @brief Cached platform type byte from CMOS/hardware. Address: 0xC38. */
STATIC UINT8 mPlatformType = 0;

// =============================================================================
// PSLT Output Buffers (.data initialized data)
// =============================================================================

/**
 * PSLT buffer 1 -- slot data output. Address: 0xBB0 (32 bytes).
 *
 * Template: Signature="PSLT", Version=1, Length=3024, StructSize=1200,
 *           NumberOfSlots=1.
 * Runtime: OVERWRITTEN by UbaProtocol->GetData(gUbaSlotDataPsl1Guid, ...).
 *          After GetData, contains the live platform slot table.
 */
STATIC PSLT_HEADER mSlotDataBuffer1 = {
    .Signature     = 0x544C5350,  /* 'PSLT' */
    .Version       = 1,
    .Length        = 3024,
    .StructSize    = 1200,
    .NumberOfSlots = 1
};

/**
 * PSLT buffer 2 -- slot config output. Address: 0xBE0 (40 bytes).
 *
 * Same PSLT header as above, plus 8 bytes extended config (initialized
 * to 0x04B4 = StructSize + 4 in the template).
 * Runtime: OVERWRITTEN by UbaProtocol->GetData(gUbaSlotConfigGuid, ...).
 */
STATIC SLOT_CONFIG mSlotDataBuffer2 = {
    .Header = {
        .Signature     = 0x544C5350,
        .Version       = 1,
        .Length        = 3024,
        .StructSize    = 1200,
        .NumberOfSlots = 1
    },
    .ConfigData = 0x04B4
};

// =============================================================================
// Debug Strings (.rdata section, read-only)
// =============================================================================

STATIC CONST CHAR8 mAssertEfiErrorFormat[]  = "\nASSERT_EFI_ERROR (Status = %r)\n";  /* 0x780 */
STATIC CONST CHAR8 mAssertExprStr[]         = "!EFI_ERROR (Status)";                  /* 0x7A8 */
STATIC CONST CHAR8 mDebugIdentStr[]         = "UBA:SlotDataUpdate-TypeLightningRidgeEXECB1\n"; /* 0x7C0 */
STATIC CONST CHAR8 mAssertImageHandleStr[]  = "gImageHandle != ((void *) 0)";         /* 0x7F0 */
STATIC CONST CHAR8 mBootServicesLibPath[]   =                                          /* 0x810 */
    "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c";
STATIC CONST CHAR8 mAssertSystemTableStr[]  = "gST != ((void *) 0)";                  /* 0x860 */
STATIC CONST CHAR8 mAssertBootServicesStr[] = "gBS != ((void *) 0)";                  /* 0x878 */
STATIC CONST CHAR8 mAssertRuntimeServicesStr[] = "gRT != ((void *) 0)";               /* 0x890 */
STATIC CONST CHAR8 mRuntimeServicesLibPath[] =                                          /* 0x8B0 */
    "e:\\hs\\MdePkg\\Library\\UefiRuntimeServicesTableLib\\UefiRuntimeServicesTableLib.c";
STATIC CONST CHAR8 mAssertBufferStr[]       = "Buffer != ((void *) 0)";               /* 0x900 */
STATIC CONST CHAR8 mHobLibPath[]            =                                          /* 0x918 */
    "e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c";
STATIC CONST CHAR8 mAssertHobListStr[]      = "mHobList != ((void *) 0)";             /* 0x940 */
STATIC CONST CHAR8 mUnalignedPath[]         =                                          /* 0x960 */
    "e:\\hs\\MdePkg\\Library\\BaseLib\\Unaligned.c";


// =============================================================================
// GetSlotCount (0x4B0)
// =============================================================================

/**
 * Returns the platform-specific PCIe slot count.
 *
 * LightningRidge EXEC B1 has 2 PCIe slots. This constant is consumed by
 * the UBA PSLT framework for slot enumeration and SMBIOS slot type
 * reporting.
 *
 * @return UINT8  Always 2 (number of PCIe slots on this platform).
 */
UINT8
EFIAPI
GetSlotCount (
    VOID
    )
{
    return 2;
}


// =============================================================================
// GetSlotData (0x4B4)
// =============================================================================

/**
 * Identity pass-through for slot data retrieval.
 *
 * Returns the DefaultData pointer unchanged. On EXEC B1, slot data is
 * populated by UbaProtocol->GetData directly into mSlotDataBuffer1.
 * No per-slot callback customization is needed.
 *
 * @param[in] Slot          Slot index (0-based, unused).
 * @param[in] DefaultData   Pointer to default slot data.
 * @return VOID*            The unmodified DefaultData pointer.
 */
VOID *
EFIAPI
GetSlotData (
    IN UINTN  Slot,
    IN VOID   *DefaultData
    )
{
    (VOID)Slot;
    return DefaultData;
}


// =============================================================================
// UbaGetProtocolInterface (0x4B8)
// =============================================================================

/**
 * Lazily resolves and caches the UBA DEBUG protocol interface.
 *
 * This is a TPL-safe LocateProtocol wrapper for the UBA Debug protocol
 * (GUID at 0xB60 = 36232936-0E76-31C8-A13A-3AF2FC1C3932):
 *   1. Returns cached mUbaDebugProtocol immediately if non-NULL.
 *   2. Raises TPL to TPL_HIGH_LEVEL (31) via gBS->RaiseTPL (at +0x18).
 *   3. Restores previous TPL via gBS->RestoreTPL (at +0x20).
 *   4. If previous TPL <= TPL_NOTIFY (0x10), calls
 *      gBS->LocateProtocol(&mUbaDebugProtocolGuid, NULL, &mUbaDebugProtocol).
 *   5. If LocateProtocol fails, resets mUbaDebugProtocol to NULL.
 *
 * The TPL raise/restore validates we are at or below TPL_NOTIFY, which
 * is a precondition for calling LocateProtocol as it may acquire event
 * locks (only safe at <= TPL_NOTIFY).
 *
 * The resolved debug protocol interface provides:
 *   +0x00: DebugPrint  (UINT64 Severity, CHAR8 *Format, UINT64 *VaList)
 *   +0x08: DebugAssert (CHAR8 *File, UINTN Line, CHAR8 *Expression)
 *   +0x10: Reserved (may be stub or null for this protocol)
 *
 * NOTE: This Debug protocol is SEPARATE from the UBA CONFIG protocol
 * used directly in _ModuleEntryPoint (GUID at 0xB70 = E03E0D46-...).
 *
 * @return UBA_DEBUG_PROTOCOL_INTERFACE*  Cached debug protocol, or NULL.
 */
UBA_DEBUG_PROTOCOL_INTERFACE *
EFIAPI
UbaGetProtocolInterface (
    VOID
    )
{
    UBA_DEBUG_PROTOCOL_INTERFACE  *Protocol;
    EFI_TPL                  OldTpl;

    Protocol = mUbaDebugProtocol;
    if (Protocol != NULL) {
        return Protocol;
    }

    //
    // Raise to TPL_HIGH_LEVEL, then restore to sample current TPL level.
    // LocateProtocol can only be called safely at TPL_NOTIFY or below.
    //
    OldTpl = gBootServices->RaiseTPL (TPL_HIGH_LEVEL);
    gBootServices->RestoreTPL (OldTpl);

    if (OldTpl <= TPL_NOTIFY) {
        EFI_STATUS  Status;

        Status = gBootServices->LocateProtocol (
                     &mUbaDebugProtocolGuid,
                     NULL,                          // Registration key not used
                     (VOID **)&Protocol
                     );

        if (!EFI_ERROR (Status)) {
            mUbaDebugProtocol = Protocol;
        } else {
            mUbaDebugProtocol = NULL;
            Protocol = NULL;
        }
    }

    return Protocol;
}


// =============================================================================
// UbaDebugPrint (0x538)
// =============================================================================

/**
 * Debug output routed through the UBA protocol, filtered by CMOS register
 * 0x4B verbosity level and platform type.
 *
 * Algorithm:
 *   1. Get UBA protocol via UbaGetProtocolInterface(); return if NULL.
 *   2. Read CMOS[0x4B] via I/O ports 0x70/0x71 (preserving NMI bit 7).
 *   3. Determine type/mask:
 *
 *      CMOS[0x4B] <= 3: Direct verbosity level (0=no output, 1=DE|DI,
 *                        2+=DE|DV).
 *      CMOS[0x4B] >  3: Platform type. Check cached mPlatformType.
 *                        If 0 (first access), derive from hardware strap
 *                        at MMIO 0xFDAF0490:
 *                          type = (MmioRead8(0xFDAF0490) & 2) | 1
 *                        Cached type then used: all >= 1 get DE|DV mask.
 *
 *   4. If (DerivedMask & CallerDebugLevel) != 0, call protocol DebugPrint.
 *
 * @param[in] DebugLevel  Severity mask (e.g., 0x8000000000000001 for ERROR).
 * @param[in] Format      Print format string.
 * @param[in] ...         Variable arguments for the format string.
 */
VOID
EFIAPI
UbaDebugPrint (
    IN UINT64       DebugLevel,
    IN CONST CHAR8  *Format,
    ...
    )
{
    UBA_DEBUG_PROTOCOL_INTERFACE  *Protocol;
    UINT64                  DebugMask;
    UINT8                   CmosData;
    UINT8                   NmiSave;
    UINT8                   PlatformType;
    VA_LIST                 VaList;

    Protocol = UbaGetProtocolInterface ();
    if (Protocol == NULL) {
        return;
    }

    //
    // Read CMOS register 0x4B.
    // Preserve the NMI enable bit (bit 7) through the index write.
    //
    NmiSave  = IoRead8 (CMOS_INDEX_PORT);
    IoWrite8 (CMOS_INDEX_PORT, (NmiSave & BIT7) | CMOS_PLATFORM_TYPE_REG);
    CmosData = IoRead8 (CMOS_DATA_PORT);

    //
    // Translate CMOS value into platform type for debug mask selection.
    //
    if (CmosData > 3) {
        //
        // Values > 3 are platform type indicators. Use cached type, or
        // derive from hardware strap if first access.
        //
        PlatformType = mPlatformType;
        if (PlatformType == 0) {
            //
            // Read hardware strap register from PCH MMIO.
            // Bit 1 indicates platform variant; OR with 1 ensures non-zero.
            //
            PlatformType = (MmioRead8 (PLATFORM_STRAP_MMIO_ADDR) & 0x02) | 0x01;
            mPlatformType = PlatformType;
        }
    } else {
        //
        // Values 0-3 are direct verbosity levels.
        //
        PlatformType = CmosData;
    }

    //
    // Map platform type to debug severity mask.
    // Type >= 1 produces either DE|DI (type 1) or DE|DV (type 2+).
    // Type 0 suppresses all output.
    //
    if (PlatformType >= 1) {
        if (PlatformType == 1) {
            DebugMask = DEBUG_INFO_MASK;     // 0x80000004: DebugError | DebugInfo
        } else {
            DebugMask = DEBUG_VERBOSE_MASK;  // 0x80000006: DebugError | DebugVerbose
        }
    } else {
        DebugMask = 0;
    }

    //
    // Forward to protocol DebugPrint if severity matches.
    //
    if ((DebugMask & DebugLevel) != 0) {
        VA_START (VaList, Format);
        Protocol->DebugPrint (DebugLevel, (CHAR8 *)Format, (UINT64 *)&VaList);
        VA_END (VaList);
    }
}


// =============================================================================
// UbaDebugAssert (0x5C0)
// =============================================================================

/**
 * ASSERT failure handler via the UBA protocol.
 *
 * Locates the UBA protocol and calls its DebugAssert function (at +0x08)
 * with the file name, line number, and assertion expression. Returns
 * silently if the UBA protocol is not available.
 *
 * @param[in] FileName     Source file path where the assertion failed.
 * @param[in] LineNumber   Line number of the failed assertion.
 * @param[in] Expression   The assertion expression string.
 */
VOID
EFIAPI
UbaDebugAssert (
    IN CONST CHAR8  *FileName,
    IN UINTN         LineNumber,
    IN CONST CHAR8  *Expression
    )
{
    UBA_DEBUG_PROTOCOL_INTERFACE  *Protocol;

    Protocol = UbaGetProtocolInterface ();
    if (Protocol != NULL) {
        Protocol->DebugAssert (
            (CHAR8 *)FileName,
            LineNumber,
            (CHAR8 *)Expression
            );
    }
}


// =============================================================================
// GetHobList (0x600)
// =============================================================================

/**
 * Locates and caches the HOB list from SystemTable->ConfigurationTable.
 *
 * Walks the EFI Configuration Table array comparing each entry's
 * VendorGuid against gEfiDxeServicesTableGuid
 * ({7739F24C-93D7-11D4-9A3A-0090273FC14D}). The comparison uses
 * IsHobGuidMatch which reads GUIDs as two 64-bit unaligned values.
 *
 * On match, caches the VendorTable pointer (the HOB list) in mHobList.
 * If no match or table empty:
 *   - Prints ASSERT_EFI_ERROR via UbaDebugPrint with EFI_NOT_FOUND.
 *   - Fires DebugAssert at HobLib.c:54 and :55.
 *
 * @return VOID*  The HOB list pointer, or NULL if not found.
 */
VOID *
EFIAPI
GetHobList (
    VOID
    )
{
    UINTN                   Index;
    EFI_CONFIGURATION_TABLE *ConfigTable;
    UINTN                   TableEntryCount;

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

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

    mHobList = NULL;

    if (ConfigTable != NULL) {
        for (Index = 0; Index < TableEntryCount; Index++) {
            if (IsHobGuidMatch (
                    &mEfiDxeServicesTableGuid,
                    &ConfigTable[Index].VendorGuid
                    ))
            {
                mHobList = ConfigTable[Index].VendorTable;
                break;
            }
        }
    }

    //
    // If HOB list not found, assert with EFI_NOT_FOUND.
    //
    if (mHobList == NULL) {
        UbaDebugPrint (
            DEBUG_ERROR,
            mAssertEfiErrorFormat,
            (UINT64)EFI_NOT_FOUND
            );
        UbaDebugAssert (
            mHobLibPath,
            54,
            mAssertExprStr
            );
    }

    if (mHobList == NULL) {
        UbaDebugAssert (
            mHobLibPath,
            55,
            mAssertHobListStr
            );
    }

    return mHobList;
}


// =============================================================================
// IsHobGuidMatch (0x6D8)
// =============================================================================

/**
 * Compares two EFI_GUID values as two 64-bit unsigned integer pairs.
 *
 * Reads bytes 0-7 and 8-15 of each GUID via ReadUnaligned64() and
 * compares both halves. This matches the standard EDK2 GUID comparison
 * pattern and avoids unaligned access faults on strict-alignment CPUs.
 *
 * @param[in] Guid1  Pointer to first EFI_GUID.
 * @param[in] Guid2  Pointer to second EFI_GUID.
 * @return BOOLEAN   TRUE if both 64-bit halves match, FALSE otherwise.
 */
BOOLEAN
EFIAPI
IsHobGuidMatch (
    IN EFI_GUID  *Guid1,
    IN EFI_GUID  *Guid2
    )
{
    UINT64 Value1A, Value1B;
    UINT64 Value2A, Value2B;

    Value1A = ReadUnaligned64 ((CONST UINT64 *)Guid1);
    Value1B = ReadUnaligned64 ((CONST UINT64 *)((UINT8 *)Guid1 + 8));

    Value2A = ReadUnaligned64 ((CONST UINT64 *)Guid2);
    Value2B = ReadUnaligned64 ((CONST UINT64 *)((UINT8 *)Guid2 + 8));

    return (BOOLEAN)(Value1A == Value2A && Value1B == Value2B);
}


// =============================================================================
// ReadUnaligned64 (0x748)
// =============================================================================

/**
 * Reads a UINT64 from a potentially unaligned address with NULL
 * pointer assertion.
 *
 * Validates Buffer != NULL via UbaDebugAssert, then performs a direct
 * 64-bit read. On x64 the CPU natively handles unaligned loads; on
 * strict-alignment architectures the compiler may emit byte-by-byte
 * loads for this dereference.
 *
 * @param[in] Buffer  Pointer to UINT64 (must not be NULL).
 * @return UINT64     The value read from Buffer.
 */
UINT64
EFIAPI
ReadUnaligned64 (
    IN CONST UINT64  *Buffer
    )
{
    if (Buffer == NULL) {
        UbaDebugAssert (
            mUnalignedPath,
            192,
            mAssertBufferStr
            );
    }

    return *Buffer;
}


// =============================================================================
// _ModuleEntryPoint (0x390)
// =============================================================================

/**
 * UEFI DXE driver entry point.
 *
 * Called by the DXE Core dispatcher. Performs initialization in six
 * sequential phases:
 *
 *   Phase 1 - Cache globals:
 *     Saves ImageHandle, SystemTable, BootServices, RuntimeServices.
 *     Each pointer is NULL-asserted via UbaDebugAssert against its
 *     respective library file path.
 *
 *   Phase 2 - HOB list init:
 *     Calls GetHobList() to find the HOB list from the configuration
 *     table. Required before any HOB-based data access.
 *
 *   Phase 3 - Debug identification:
 *     Logs "UBA:SlotDataUpdate-TypeLightningRidgeEXECB1\n" if debug
 *     output is enabled by CMOS[0x4B] configuration.
 *
 *   Phase 4 - Locate UBA protocol:
 *     gBS->LocateProtocol(&mUbaDebugProtocolGuid, NULL, &UbaProtocol).
 *     Returns EFI_NOT_FOUND if no UBA protocol is installed.
 *
 *   Phase 5 - Get PSLT entry 1 (slot data):
 *     UbaProtocol->GetData(This, &mUbaSlotDataPsl1Guid,
 *                          &mSlotDataBuffer1, sizeof(PSLT_HEADER)).
 *     Populates mSlotDataBuffer1 with platform PSLT data.
 *
 *   Phase 6 - Get PSLT entry 2 (slot config):
 *     UbaProtocol->GetData(This, &mUbaSlotConfigGuid,
 *                          &mSlotDataBuffer2, sizeof(SLOT_CONFIG)).
 *     Populates mSlotDataBuffer2 with extended slot config.
 *
 * Each GetData failure returns immediately with the error status --
 * phase 6 is only reached if phase 5 succeeded.
 *
 * @param[in] ImageHandle  Firmware-allocated handle for this image.
 * @param[in] SystemTable  Pointer to the EFI System Table.
 * @return EFI_STATUS      EFI_SUCCESS on success, or error from
 *                         LocateProtocol or either GetData call.
 */
EFI_STATUS
EFIAPI
_ModuleEntryPoint (
    IN EFI_HANDLE        ImageHandle,
    IN EFI_SYSTEM_TABLE  *SystemTable
    )
{
    EFI_STATUS               Status;
    UBA_CONFIG_PROTOCOL_INTERFACE   *UbaProtocol;

    // ---------------------------------------------------------------------
    // Phase 1: Cache and validate global service table pointers.
    // ---------------------------------------------------------------------

    gImageHandle = ImageHandle;
    if (ImageHandle == NULL) {
        UbaDebugAssert (mBootServicesLibPath, 51, mAssertImageHandleStr);
    }

    gSystemTable = SystemTable;
    if (SystemTable == NULL) {
        UbaDebugAssert (mBootServicesLibPath, 57, mAssertSystemTableStr);
    }

    gBootServices = SystemTable->BootServices;
    if (gBootServices == NULL) {
        UbaDebugAssert (mBootServicesLibPath, 63, mAssertBootServicesStr);
    }

    gRuntimeServices = SystemTable->RuntimeServices;
    if (gRuntimeServices == NULL) {
        //
        // NOTE: RT check uses UefiRuntimeServicesTableLib.c line 47,
        // while the other three use UefiBootServicesTableLib.c.
        // This reflects the different library each global comes from.
        //
        UbaDebugAssert (mRuntimeServicesLibPath, 47, mAssertRuntimeServicesStr);
    }

    // ---------------------------------------------------------------------
    // Phase 2: Initialize HOB list from configuration table.
    // ---------------------------------------------------------------------

    GetHobList ();

    // ---------------------------------------------------------------------
    // Phase 3: Emit driver identification into debug output.
    // ---------------------------------------------------------------------

    UbaDebugPrint (DEBUG_ERROR, mDebugIdentStr);

    // ---------------------------------------------------------------------
    // Phase 4: Locate the UBA CONFIG protocol interface.
    //          This uses the PROTOCOL GUID AT 0xB70 (E03E0D46-...),
    //          which is SEPARATE from the DEBUG protocol at 0xB60
    //          used by UbaGetProtocolInterface/DebugPrint/DebugAssert.
    //
    //          The config protocol provides GetData at vtable +0x10,
    //          which reads PSLT entries from the UBA configuration store.
    // ---------------------------------------------------------------------

    UbaProtocol = NULL;
    Status = gBootServices->LocateProtocol (
                 &mUbaConfigProtocolGuid,
                 NULL,                              // No registration key
                 (VOID **)&UbaProtocol
                 );

    if (EFI_ERROR (Status)) {
        return Status;
    }

    // ---------------------------------------------------------------------
    // Phase 5: Read PSLT entry 1 (slot data) from UBA configuration.
    // ---------------------------------------------------------------------

    Status = UbaProtocol->GetData (
                 UbaProtocol,
                 &mUbaSlotDataPsl1Guid,
                 &mSlotDataBuffer1,
                 sizeof (mSlotDataBuffer1)          // 32 bytes = PSLT_HEADER
                 );

    if (EFI_ERROR (Status)) {
        return Status;
    }

    // ---------------------------------------------------------------------
    // Phase 6: Read PSLT entry 2 (slot config) from UBA configuration.
    // ---------------------------------------------------------------------

    Status = UbaProtocol->GetData (
                 UbaProtocol,
                 &mUbaSlotConfigGuid,
                 &mSlotDataBuffer2,
                 sizeof (mSlotDataBuffer2)          // 40 bytes = SLOT_CONFIG
                 );

    return Status;
}