/**
* @file OpromUpdateDxeLightningRidgeEXECB3.c
*
* @brief OpROM Update DXE Driver for LightningRidge EXEC B3 Platform.
*
* This is a UBA (Unified Board Architecture) board-type DXE driver that
* registers PCIe Option ROM (OpROM) update configuration tables for the
* LightningRidge EXEC B3 server platform (Intel Purley family).
*
* == Architecture Overview ==
* The driver follows the standard UBA board-type pattern:
* 1. Entry point caches UEFI globals from SystemTable
* 2. HOB list is resolved via ConfigurationTable for platform info
* 3. Platform debug banner ("UBA:OpromUpdate-TypeLightningRidgeEXECB3") is emitted
* 4. UBA_CONFIG_PROTOCOL (GUID {E03E0D46...}) is located via gBS->LocateProtocol()
* 5. A PBDS structure (48 bytes) with 5 callbacks is registered via RegisterConfig
*
* The 5 callbacks registered in the PBDS structure:
* [0] @+0x08: IsPcieSlotConfigured() - Slot range validation via PCI config space
* [1] @+0x10: OpromGetConfigA() - Zero-initialized table (terminator)
* [2] @+0x18: OpromGetConfigB() - PCIe config table (6 entries)
* [3] @+0x20: OpromGetConfigC() - Extended PCIe config table (10 entries)
* [4] @+0x28: OpromSetSlotNumber() - Slot number setter (always 0)
*
* == Key Differences from EXECB1/EXECB2 ==
* - Platform string: "UBA:OpromUpdate-TypeLightningRidgeEXECB3"
* - PDB: OpromUpdateDxeLightningRidgeEXECB3.pdb
* - Otherwise structurally identical (same GUIDs, same callbacks, same data tables)
*
* == Binary Info ==
* - Index: 31 of 427 PE files in HR650X BIOS
* - SHA256: d58f84d1adb92017d59b450e242cac64939aadc6e24a35da7573064e7b531022
* - PDB GUID: {2E401ABE-49E9-4BD3-969B-AF5A00349AA6}, Age 1
* - Source path: e:\hs\PurleyRpPkg\Uba\UbaMain\Dxe\TypeLightningRidgeEXECB3\OpromUpdateDxe\
* - Build: HR6N0XMLK\DEBUG_VS2015\X64
*/
#include "OpromUpdateDxeLightningRidgeEXECB3.h"
/*==============================================================================
* GUID Definitions (stored in .rdata at fixed image offsets)
*============================================================================*/
/**
* EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_GUID
* Standard UEFI protocol for PCI Root Bridge IO operations (config space access).
* GUID: {2F707EBB-4A1A-11D4-9A38-0090273FC14D}
* Image address: 0xCE0
* Used by IsPcieSlotConfigured() via gBS->LocateProtocol().
*/
STATIC CONST EFI_GUID gEfiPciRootBridgeIoProtocolGuid =
EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_GUID;
/**
* UBA_DEBUG_LIB_PROTOCOL_GUID
* Debug library protocol for UBA framework debug output and assertions.
* GUID: {36232936-0E76-31C8-A13A-3AF2FC1C3932}
* Image address: 0xCF0
* Resolved lazily by GetDebugLibProtocol().
*/
STATIC CONST EFI_GUID gUbaDebugLibProtocolGuid =
UBA_DEBUG_LIB_PROTOCOL_GUID;
/**
* UBA_CONFIG_PROTOCOL_GUID
* Board-type configuration protocol for LightningRidge EXEC B3.
* Provides RegisterConfig function at vtable offset 0x10.
* GUID: {E03E0D46-5263-4845-B0A4-58D57B3177E2}
* Image address: 0xD00
*/
STATIC CONST EFI_GUID gUbaConfigProtocolGuid =
UBA_CONFIG_PROTOCOL_GUID;
/**
* EFI_HOB_LIST_GUID
* Used to locate the HOB (Hand-Off Block) list via SystemTable->ConfigurationTable.
* GUID: {7739F24C-93D7-11D4-9A3A-0090273FC14D}
* Image address: 0xD10 (first 8 bytes), 0xD18 (second 8 bytes)
*/
STATIC CONST EFI_GUID gEfiHobListGuid =
EFI_HOB_LIST_GUID;
/**
* OPROM_UPDATE_CONFIG_DATA_GUID
* Configuration type GUID for the PBDS structure.
* Passed to UBA_CONFIG_PROTOCOL.RegisterConfig().
* GUID: {371BD79C-DE79-4C5F-AA2B-BC9EBEFA988F}
* Image address: 0xEF0
*/
STATIC CONST EFI_GUID gOpromUpdateConfigDataGuid =
OPROM_UPDATE_CONFIG_DATA_GUID;
/*==============================================================================
* Global Variable Definitions (.data segment, image addr 0xF30-0xF5F)
*============================================================================*/
//
// UEFI standard protocol pointer caches (set by _ModuleEntryPoint)
// Image addr 0xF30: gST
// Image addr 0xF38: gBS
// Image addr 0xF40: gImageHandle
// Image addr 0xF48: gRT
//
EFI_SYSTEM_TABLE *gST = NULL;
EFI_BOOT_SERVICES *gBS = NULL;
EFI_HANDLE gImageHandle = NULL;
EFI_RUNTIME_SERVICES *gRT = NULL;
//
// Cached protocol/hand-off block pointers (resolved lazily)
// Image addr 0xF50: gOpromProtocol (UBA DebugLib protocol cache)
// Image addr 0xF58: gHobList (HOB list pointer cache)
//
UBA_DEBUG_LIB_PROTOCOL *gOpromProtocol = NULL;
VOID *gHobList = NULL;
/*==============================================================================
* Configuration Data Structures
*============================================================================*/
/**
* PCIE_SLOT_RANGE table (image addr 0xED0, 8 entries of 4 bytes = 32 bytes)
*
* Encodes PCIe BDF slot ranges for 8 possible slots.
* Read by IsPcieSlotConfigured() using byte-level stride of 4:
* Byte[-1] = Bus (or flags)
* Byte[0] = Device number
* Byte[+1] = Function number (or continuation)
*
* The combined BDF address: ((Device | (Function << 8)) << 8 | Bus) << 8
* Entry 0: Bus=0x00, Dev=0x03, Func=0x00 -> 0x30000 (BDF 00:03.0)
* Entry 1: Bus=0x00, Dev=0x01, Func=0x80 -> 0x80010000 (BDF 00:01.0 with flag?)
* Entry 2: Bus=0x00, Dev=0x02, Func=0x80 -> 0x80020000 (BDF 00:02.0 with flag?)
* Entry 3: Bus=0x02, Dev=0x03, Func=0x80 -> 0x80030200 (BDF 02:03.2 with flag?)
* Entry 4: Bus=0x00, Dev=0x02, Func=0x00 -> 0x20000 (BDF 00:02.0)
* Entry 5: Bus=0x00, Dev=0x01, Func=0x00 -> 0x10000 (BDF 00:01.0)
* Entry 6: Bus=0x05, Dev=0x1c, Func=0x00 -> 0x1c0500 (BDF 05:1c.0)
* Entry 7: Bus=0x00, Dev=0x03, Func=0x80 -> 0x80030000 (BDF 00:03.0 with flag?)
*
* The Func=0x80 entries (entries 1,2,3,7) appear to encode a secondary
* bus segment indicator (bit 7 set) rather than an actual function number.
*/
STATIC CONST PCIE_SLOT_RANGE mPcieSlotRanges[8] = {
{ 0x00, 0x03, 0x00, 0x00 }, /* Entry 0: BDF 00:03.0 */
{ 0x00, 0x01, 0x80, 0x00 }, /* Entry 1: BDF 00:01.0 (seg 0x80) */
{ 0x00, 0x02, 0x80, 0x00 }, /* Entry 2: BDF 00:02.0 (seg 0x80) */
{ 0x02, 0x03, 0x80, 0x00 }, /* Entry 3: BDF 02:03.2 (seg 0x80) */
{ 0x00, 0x02, 0x00, 0x00 }, /* Entry 4: BDF 00:02.0 */
{ 0x00, 0x01, 0x00, 0x00 }, /* Entry 5: BDF 00:01.0 */
{ 0x05, 0x1C, 0x00, 0x00 }, /* Entry 6: BDF 05:1C.0 */
{ 0x00, 0x03, 0x80, 0x00 }, /* Entry 7: BDF 00:03.0 (seg 0x80) */
};
/**
* Config Table A (image addr 0xD20, 64 bytes of zeros)
*
* Primary PCIe topology table. All zeros on this platform.
* Acts as a terminator/sentinel - the UBA framework interprets this
* as "no primary configuration". The function OpromGetConfigA() simply
* returns a pointer to this zeroed area.
*
* The 0xFFFFFFFF at the end (offset 0xD50) might be a terminator value.
*/
STATIC CONST UINT64 mConfigTableA[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
/**
* Config Table B (image addr 0xD60, 6 entries of 8 bytes = 48 bytes)
*
* Device configuration entries for PCIe root ports and endpoints.
* Each entry is 8 bytes, encoding:
* Bytes 0-3: Entry descriptor (type, flags, bus/device/function)
* Bytes 4-7: Extended descriptor (vendor/device ID, range info)
*
* Raw data (little-endian 64-bit values):
* [0] 0x0002000100000001 Type=1, flags=0x00020001, ...
* [1] 0x4801080215288086 Vendor=0x8086, Device=0x1528 (Intel I350)
* [2] 0x0000002d01000000 Revision=0, Class=0x01000000
* [3] 0x0000002c00000001 SubVendor=0x0001, SubSystem=0x0000002c
* [4] 0xff2fff2e00000001 Slot range: 0x2e-0x2f (or 0xff2f-0xff)
* [5] 0x0200010000000100 Extended config
*/
STATIC CONST UINT64 mConfigTableB[6] = {
0x0002000100000001ULL,
0x4801080215288086ULL,
0x0000002d01000000ULL,
0x0000002c00000001ULL,
0xff2fff2e00000001ULL,
0x0200010000000100ULL,
};
/**
* Config Table C (image addr 0xE60, 10 entries of 8 bytes = 80 bytes)
*
* Extended device configuration entries.
* These appear to describe Intel Ethernet controller configurations
* including Intel I350 (0x1521, 0x1522) and 82599ES (0x10D3, 0x10FB).
*
* Raw data (little-endian 64-bit values):
* [0] 0x00010100358010d3 Dev=0x10D3:3580 (Intel 82599ES)
* [1] 0x0100358015210000 Dev=0x1521:3580 (Intel I350 variant)
* [2] 0x35b0152100000001 Dev=0x1521:35B0 (Intel I350 variant)
* [3] 0x1522000000010100 Dev=0x1522:3580 (Intel I350 variant)
* [4] 0x0000000101003580 Dev=0x...:3580 (Intel I350)
* [5] 0x0001010035561521 Dev=0x1521:3556 (Intel I350)
* [6] 0x0101355710fb0000 Dev=0x10FB:3557 (Intel 82599ES)
* [7] 0x3558152800000101 Dev=0x1528:3558 (Intel I350)
* [8] 0x1528000001010101 Dev=0x1528 (Intel I350)
* [9] 0x00000101010135c5 Extended entry
*/
STATIC CONST UINT64 mConfigTableC[10] = {
0x00010100358010d3ULL,
0x0100358015210000ULL,
0x35b0152100000001ULL,
0x1522000000010100ULL,
0x0000000101003580ULL,
0x0001010035561521ULL,
0x0101355710fb0000ULL,
0x3558152800000101ULL,
0x1528000001010101ULL,
0x00000101010135c5ULL,
};
/**
* OPROM_UPDATE_CONFIG (PBDS structure at image addr 0xF00, 48 bytes)
*
* Registered with the UBA_CONFIG_PROTOCOL.RegisterConfig() call in
* _ModuleEntryPoint. Contains 5 callback function pointers that the
* UBA framework uses to query platform-specific OpROM update info.
*
* The .reloc section has entries for the address range 0xF08-0xF28
* (the function pointer fields), ensuring proper relocation at runtime.
*/
STATIC CONST OPROM_UPDATE_CONFIG mOpromUpdateConfig = {
.Signature = 0x53444250, /* 'PBDS' */
.Version = 1,
.IsPcieSlotConfigured = (UINT64)(UINTN)IsPcieSlotConfigured,
.OpromGetConfigA = (UINT64)(UINTN)OpromGetConfigA,
.OpromGetConfigB = (UINT64)(UINTN)OpromGetConfigB,
.OpromGetConfigC = (UINT64)(UINTN)OpromGetConfigC,
.OpromSetSlotNumber = (UINT64)(UINTN)OpromSetSlotNumber,
};
/*==============================================================================
* Function Implementations
*============================================================================*/
/**
* @brief UEFI DXE driver entry point.
*
* Called by the DXE Dispatcher after all dependencies are resolved.
*
* Initialization sequence:
* 1. Cache ImageHandle, SystemTable, BootServices, RuntimeServices globals
* 2. Resolve the HOB list via GetHobList() (uses SystemTable->ConfigurationTable)
* 3. Emit the platform identification banner via DebugPrint()
* 4. Locate UBA_CONFIG_PROTOCOL by GUID {E03E0D46-5263-4845-B0A4-58D57B3177E2}
* 5. Call the protocol's RegisterConfig (vtable[2] @ offset 0x10) with:
* - Config type GUID: {371BD79C-DE79-4C5F-AA2B-BC9EBEFA988F}
* - Config data: &mOpromUpdateConfig (PBDS, 48 bytes)
* - Data size: 48 (sizeof(OPROM_UPDATE_CONFIG))
*
* @param[in] ImageHandle UEFI image handle for this driver.
* @param[in] SystemTable UEFI system table pointer.
*
* @return EFI_SUCCESS Configuration registered successfully.
* @return EFI_UNSUPPORTED/... LocateProtocol or RegisterConfig failure.
*
* @note The UefiBootServicesTableLib and UefiRuntimeServicesTableLib
* library constructors set the module-level gImageHandle, gST,
* gBS, and gRT globals. The entry point's explicit caching here
* duplicates that functionality for safety.
*/
EFI_STATUS
EFIAPI
_ModuleEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
VOID *UbaConfigProtocol; /* Protocol interface from LocateProtocol */
UINT64 ConfigBuffer; /* Output buffer for LocateProtocol */
//
// Step 1: Cache UEFI standard protocol pointers.
// These are also set by the library constructors in
// UefiBootServicesTableLib and UefiRuntimeServicesTableLib,
// but we cache them explicitly here for robustness.
//
gImageHandle = ImageHandle;
gST = SystemTable;
gBS = SystemTable->BootServices;
gRT = SystemTable->RuntimeServices;
//
// Step 2: Resolve the HOB (Hand-Off Block) list.
// This walks SystemTable->ConfigurationTable[] looking for the entry
// with VendorGuid == EFI_HOB_LIST_GUID ({7739F24C-...}).
// The associated VendorTable pointer is the HOB list header.
// Result is cached in gHobList (image addr 0xF58).
//
GetHobList ();
//
// Step 3: Print the platform identification banner.
// ErrorLevel 0x80000000 is the UBA-specific debug severity mask.
//
DebugPrint (0x80000000, "UBA:OpromUpdate-TypeLightningRidgeEXECB3\n");
//
// Step 4: Locate the UBA_CONFIG_PROTOCOL.
// GUID {E03E0D46-5263-4845-B0A4-58D57B3177E2} identifies the
// platform-specific UBA configuration protocol.
//
ConfigBuffer = 0;
Status = gBS->LocateProtocol (
&gUbaConfigProtocolGuid,
NULL, /* No registration */
(VOID **)&ConfigBuffer /* Output interface */
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Step 5: Call the protocol's RegisterConfig function.
// The function pointer is at vtable index 2 (offset 0x10).
// Signature:
// EFI_STATUS (*RegisterConfig)(
// VOID *This,
// CONST EFI_GUID *ConfigGuid,
// CONST VOID *ConfigData,
// UINTN ConfigDataSize
// );
//
// Parameters:
// ConfigGuid = &gOpromUpdateConfigDataGuid
// {371BD79C-DE79-4C5F-AA2B-BC9EBEFA988F}
// ConfigData = &mOpromUpdateConfig (PBDS structure, 48 bytes)
// DataSize = 48 (sizeof(OPROM_UPDATE_CONFIG))
//
UbaConfigProtocol = (VOID *)ConfigBuffer;
return ((EFI_STATUS (*)(VOID *, CONST EFI_GUID *, CONST VOID *, UINTN))
(*((UINT64 **)UbaConfigProtocol) + 2)) ( /* vtable[2] = offset 0x10 */
UbaConfigProtocol,
&gOpromUpdateConfigDataGuid,
&mOpromUpdateConfig,
sizeof (OPROM_UPDATE_CONFIG)
);
}
/**
* @brief Lazily resolves and caches the UBA DebugLib protocol.
*
* Called on first use from DebugPrint() or DebugAssert(). The protocol
* is resolved only once and cached in gOpromProtocol (image addr 0xF50).
*
* Resolution steps:
* 1. Check cache: if gOpromProtocol is non-NULL, return immediately
* 2. Pool check: allocate and free a 31-byte pool. If only <= 0x10 bytes
* available, return NULL (minimum pool size sanity check)
* 3. LocateProtocol: call gBS->LocateProtocol() with
* UBA_DEBUG_LIB_PROTOCOL_GUID {36232936-0E76-31C8-A13A-3AF2FC1C3932}
* 4. On success, cache the result and return; on failure, cache NULL
*
* @return Pointer to UBA_DEBUG_LIB_PROTOCOL, or NULL if:
* - Pool allocation check fails (<= 0x10 bytes)
* - gBS->LocateProtocol() fails
*
* @note The pool allocation/free sequence (UINTN type 31, then free) is
* a UEFI memory subsystem sanity check inherited from the library.
*/
UBA_DEBUG_LIB_PROTOCOL *
GetDebugLibProtocol (
VOID
)
{
UINTN PoolSize; /* Dummy pool allocation for size check */
UINT64 ProtocolPtr; /* Output buffer for LocateProtocol */
//
// Return cached pointer if already resolved.
//
if (gOpromProtocol != NULL) {
return gOpromProtocol;
}
//
// Check available pool size by allocating and immediately freeing.
// Type 31 is EfiBootServicesData in this platform's memory map.
// If only <= 0x10 bytes available, return NULL.
//
gBS->AllocatePool (EfiBootServicesData, 31, (VOID **)&PoolSize);
gBS->FreePool ((VOID *)PoolSize);
if (PoolSize <= 0x10) {
gOpromProtocol = NULL;
return NULL;
}
//
// Resolve the UBA DebugLib protocol.
//
ProtocolPtr = 0;
if (EFI_ERROR (gBS->LocateProtocol (
&gUbaDebugLibProtocolGuid,
NULL,
(VOID **)&ProtocolPtr
))) {
gOpromProtocol = NULL;
return NULL;
}
gOpromProtocol = (UBA_DEBUG_LIB_PROTOCOL *)ProtocolPtr;
return gOpromProtocol;
}
/**
* @brief Platform-aware debug output with CMOS-based board type detection.
*
* Reads CMOS register 0x4B (via legacy I/O ports 0x70/0x71) to determine
* the board platform type, which selects the debug output mask:
*
* Platform type 1 (LightningRidge):
* Debug mask = 0x80000004
* Platform type 2 or 3 (other platforms):
* Debug mask = 0x80000006
*
* If CMOS register 0x4B reads as 0 (uninitialized), falls back to MMIO
* read at physical address 0xFDAF0490 bit 1, ORed with 1, to determine
* the platform type.
*
* Debug output is only emitted when (ErrorLevel & DebugMask) != 0.
* The function returns 4 (not 0) when output is suppressed but the
* protocol and platform check succeeded - this distinguishes "suppressed"
* from "protocol missing".
*
* @param[in] ErrorLevel Debug message severity level mask.
* @param[in] Format Printf-compatible format string.
* @param[in] ... Variable arguments for format string.
*
* @return Non-zero Debug output was emitted or successfully suppressed.
* @return 0 DebugLib protocol not available (GetDebugLibProtocol failed).
*/
UINTN
EFIAPI
DebugPrint (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Format,
...
)
{
VA_LIST VaList;
UBA_DEBUG_LIB_PROTOCOL *Protocol;
UINTN PlatformType; /* CMOS platform type value */
UINTN DebugMask; /* Platform-specific debug mask */
//
// Resolve the DebugLib protocol (lazy initialization).
//
Protocol = GetDebugLibProtocol ();
if (Protocol == NULL) {
return 0;
}
//
// Read CMOS register 0x4B to determine the board platform type.
// CMOS access sequence:
// 1. Read index register (0x70) to preserve NMI disable bit
// 2. Write index with NMI-disabled + 0x4B
// 3. Read data register (0x71)
//
PlatformType = IoRead8 (0x70); /* Save NMI state */
IoWrite8 (0x70, (PlatformType & 0x80) | 0x4B); /* Select CMOS 0x4B */
PlatformType = IoRead8 (0x71); /* Read platform type */
//
// If CMOS returned > 3 and == 0, the register is uninitialized.
// Fall back to MMIO at 0xFDAF0490 (GPIO or strapping register).
// Extract bit 1, OR with 1 to get platform type 1 or 3.
//
if (PlatformType > 3) {
if (PlatformType == 0) {
PlatformType = (*(volatile UINT8 *)0xFDAF0490 & 2) | 1;
}
}
//
// Select debug mask based on platform type.
//
if (PlatformType == 1) {
DebugMask = 0x80000004; /* LightningRidge specific mask */
} else {
DebugMask = 0x80000006; /* Other platform mask */
}
//
// Check if the error level matches the platform debug mask.
// If no bits match, suppress the output.
//
if ((DebugMask & ErrorLevel) == 0) {
return 4; /* Successfully suppressed (not error) */
}
//
// Call the protocol's DebugPrint function at vtable offset 0x00.
// The protocol's DebugPrint uses VA_LIST, not variadic args.
//
VA_START (VaList, Format);
return ((UINTN (*)(UINTN, CONST CHAR8 *, VA_LIST))
Protocol->DebugPrint) (ErrorLevel, Format, VaList);
}
/**
* @brief ASSERT failure handler via DebugLib protocol.
*
* Called when an ASSERT() condition evaluates to FALSE in the module.
* Routes to the UBA DebugLib protocol's assertion handler at protocol
* vtable offset 0x08.
*
* The assertion information includes the source file path, line number,
* and a text description of the assertion condition.
*
* @param[in] FileName Full path to the source file containing the assert.
* @param[in] LineNumber Line number of the assertion in the source file.
* @param[in] Description Text description of the failing assertion condition.
*/
VOID
DebugAssert (
IN CONST CHAR8 *FileName,
IN UINTN LineNumber,
IN CONST CHAR8 *Description
)
{
UBA_DEBUG_LIB_PROTOCOL *Protocol;
//
// Resolve the DebugLib protocol (lazy initialization).
//
Protocol = GetDebugLibProtocol ();
if (Protocol != NULL) {
//
// Call the protocol's assertion handler at vtable offset 0x08.
//
((VOID (*)(CONST CHAR8 *, UINTN, CONST CHAR8 *))
Protocol->DebugAssert) (FileName, LineNumber, Description);
}
}
/**
* @brief Locate the HOB (Hand-Off Block) list from UEFI configuration table.
*
* The HOB list pointer is stored in the UEFI system table's configuration
* table array, keyed by EFI_HOB_LIST_GUID ({7739F24C-93D7-11D4-9A3A-
* 0090273FC14D}). This function scans gST->ConfigurationTable[] to find it.
*
* The system table layout for HOB access:
* gST->NumberOfTableEntries = *(UINTN *)(gST + 0x68)
* gST->ConfigurationTable = *(EFI_CONFIGURATION_TABLE **)(gST + 0x70)
* Each entry is 24 bytes: EFI_GUID (16) + VOID* (8)
*
* Once found, the result is cached in gHobList (image addr 0xF58) for
* O(1) subsequent access.
*
* Error handling:
* - If the HOB list GUID is NOT found in any ConfigurationTable entry,
* triggers DebugAssert with status 0x800000000000000E (EFI_NOT_FOUND)
* - If the resolved pointer is NULL, another DebugAssert is triggered
* - Both ASSERTs reference e:\hs\MdePkg\Library\DxeHobLib\HobLib.c
* (lines 54 and 55)
*
* @return Pointer to the HOB list, or NULL if not found / resolution fails.
*/
VOID *
GetHobList (
VOID
)
{
UINTN Index;
UINTN TableCount;
EFI_CONFIGURATION_TABLE *ConfigTable;
//
// Return cached pointer if already resolved.
//
if (gHobList != NULL) {
return gHobList;
}
gHobList = NULL;
//
// Access the configuration table array from the system table.
// SystemTable + 0x68 = NumberOfTableEntries (UINTN)
// SystemTable + 0x70 = ConfigurationTable (EFI_CONFIGURATION_TABLE *)
//
TableCount = gST->NumberOfTableEntries;
ConfigTable = gST->ConfigurationTable;
//
// Iterate through the configuration table entries to find the HOB list.
// Each entry is 24 bytes: 16-byte VendorGuid + 8-byte VendorTable pointer.
// We compare the first 8 bytes and next 8 bytes separately via
// ReadUnaligned64 against the reference GUID halves.
//
if (ConfigTable != NULL) {
for (Index = 0; Index < TableCount; Index++) {
if (IsHobListGuid (&ConfigTable[Index].VendorGuid)) {
//
// Found the HOB list GUID. The associated VendorTable pointer
// is the HOB list (PHIT HOB header).
//
gHobList = (VOID *)ConfigTable[Index].VendorTable;
break;
}
}
}
//
// If we didn't find the GUID in any table entry, trigger an ASSERT.
//
if (gHobList == NULL) {
DebugPrint (
0x80000000,
"\nASSERT_EFI_ERROR (Status = %r)\n",
0x800000000000000EULL /* EFI_NOT_FOUND status */
);
DebugAssert (
"e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
54,
"!EFI_ERROR (Status)"
);
}
//
// If the resolved pointer is NULL, trigger another ASSERT.
//
if (gHobList == NULL) {
DebugAssert (
"e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
55,
"mHobList != ((void *) 0)"
);
}
return gHobList;
}
/**
* @brief Check if a GUID matches EFI_HOB_LIST_GUID using 64-bit halves.
*
* Compares the input GUID against the reference EFI_HOB_LIST_GUID
* ({7739F24C-93D7-11D4-9A3A-0090273FC14D}) by splitting both into
* two 64-bit halves and comparing each half via ReadUnaligned64().
*
* The reference GUID halves are stored at:
* 0xD10: First 8 bytes = {7739F24C-93D7-11D4}
* 0xD18: Second 8 bytes = {9A3A0090-273FC14D}
*
* This avoids potential alignment issues that could arise from
* direct EFI_GUID struct comparison, since EFI_GUID has specific
* alignment requirements (4-byte aligned Data1).
*
* @param[in] Guid Pointer to the GUID to compare.
*
* @return TRUE The GUID matches EFI_HOB_LIST_GUID.
* @return FALSE The GUID does not match.
*/
BOOLEAN
IsHobListGuid (
IN EFI_GUID *Guid
)
{
//
// Compare the GUID as two 64-bit halves.
// First half: Data1(4) + Data2(2) + Data3(2) = 8 bytes (little-endian)
// Second half: Data4(8) = 8 bytes
//
return (ReadUnaligned64 (&gEfiHobListGuid) == ReadUnaligned64 (Guid))
&& (ReadUnaligned64 ((UINT8 *)&gEfiHobListGuid + 8)
== ReadUnaligned64 ((UINT8 *)Guid + 8));
}
/**
* @brief Read a 64-bit value from an (possibly unaligned) pointer.
*
* Directly dereferences a 64-bit pointer without alignment checks.
* Asserts via DebugAssert() if the pointer is NULL.
*
* This function is the module-level implementation of the BaseLib
* ReadUnaligned64() function, with the same behavior.
*
* Source path (from ASSERT string):
* e:\hs\MdePkg\Library\BaseLib\Unaligned.c, line 192
*
* @param[in] Buffer Pointer to read from (must not be NULL).
*
* @return The 64-bit value at the Buffer address.
*/
UINT64
ReadUnaligned64 (
IN CONST VOID *Buffer
)
{
//
// Assert if the pointer is NULL.
//
if (Buffer == NULL) {
DebugAssert (
"e:\\hs\\MdePkg\\Library\\BaseLib\\Unaligned.c",
192,
"Buffer != ((void *) 0)"
);
}
//
// Dereference the pointer directly.
//
return *(CONST UINT64 *)Buffer;
}
/**
* @brief Check if a PCIe BDF address is within a configured slot's range.
*
* UBA framework callback, invoked by the platform to determine if a
* PCIe device at the given BDF address should receive an OpROM update.
*
* Algorithm:
* 1. Iterate the 8 mPcieSlotRanges entries at image addr 0xED0
* 2. For each slot entry where the corresponding SlotMask bit is CLEAR
* (bit=0 means the slot is NOT already configured and needs checking):
* a. Compute the BDF address from the 3-byte descriptor:
* combined = ((Device | (Function << 8)) << 8 | Bus) << 8
* This creates a PciLib address offset (register field = 0)
* b. Open EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL via gBS->LocateProtocol()
* c. Read PCI config register offset 0x19 (Secondary Bus Number / BIST)
* d. Read PCI config register offset 0x1A (Subordinate Bus / Hdr Type)
* e. Check: BIST_val <= PcieBdfAddr <= HdrType_val
* (The BIST and Header Type registers are repurposed on Intel
* chipsets to encode secondary bus range information for PCIe
* root ports)
* 3. Return TRUE if any slot range contains the target BDF address
*
* Note: gBS->LocateProtocol() is called INSIDE the loop for each slot,
* not cached. This is how the original binary behaves.
*
* @param[in] PcieBdfAddr PCIe BDF address to test (bus:device:function).
* Encoded as a 64-bit PciLib address with the
* register offset bits set to 0.
* @param[in] SlotMask Bitmask of slots (bits 0-7). A clear bit means
* the slot should be checked. A set bit means
* the slot is already configured.
*
* @return TRUE The PCIe BDF address falls within a configured slot range.
* @return FALSE The BDF is outside all slot ranges, or the PCI Root Bridge
* IO protocol could not be located.
*/
BOOLEAN
IsPcieSlotConfigured (
IN UINT64 PcieBdfAddr,
IN UINT32 SlotMask
)
{
CONST UINT8 *SlotData; /* Pointer walking through slot config table */
BOOLEAN Found; /* Result accumulator */
UINT16 SlotIndex; /* Loop counter for 8 slot entries */
UINT32 CurrentMask; /* Local copy of SlotMask (updated by loop) */
//
// Initialize the table pointer to start BEFORE the first entry.
// The original code uses &unk_ED1 as the base, then reads 3 bytes
// at offsets [-1, 0, +1], advancing by 4 each iteration.
// Our implementation uses direct indexing into mPcieSlotRanges[],
// which avoids the +1 offset trick.
//
SlotData = (CONST UINT8 *)&mPcieSlotRanges;
Found = FALSE;
CurrentMask = SlotMask;
//
// Iterate each of the 8 PCIe slot range entries.
//
for (SlotIndex = 0; SlotIndex < 8; SlotIndex++) {
//
// Check if this slot bit is clear in the mask.
// A clear bit (0) means the slot is NOT yet configured and
// should be checked for device presence.
//
if (((CurrentMask >> SlotIndex) & 1) == 0) {
UINT8 BusByte; /* Byte at slot entry offset 0 (Bus) */
UINT8 DevByte; /* Byte at slot entry offset 1 (Device) */
UINT8 FuncByte; /* Byte at slot entry offset 2 (Function) */
UINT64 SlotBdfAddr; /* Computed BDF address for this slot */
UINT64 PciRootBridgeIo; /* Protocol interface from LocateProtocol */
UINT8 RegValue19; /* Value of PCI config register 0x19 */
UINT8 RegValue1A; /* Value of PCI config register 0x1A */
//
// Extract the 3 bytes from the slot entry.
// In the original assembly:
// v8 = byte[-1] (mPcieSlotRanges[SlotIndex].Bus)
// v9 = byte[0] (mPcieSlotRanges[SlotIndex].Device)
// v10 = byte[+1] (mPcieSlotRanges[SlotIndex].Function)
//
BusByte = mPcieSlotRanges[SlotIndex].Bus;
DevByte = mPcieSlotRanges[SlotIndex].Device;
FuncByte = mPcieSlotRanges[SlotIndex].Function;
//
// Compute the BDF address from the 3-byte descriptor.
// Formula: ((Device | (Function << 8)) << 8 | Bus) << 8
// This gives a PciLib-style address with register field = 0.
//
SlotBdfAddr = (((DevByte | ((UINT64)FuncByte << 8)) << 8)
| (UINT64)BusByte) << 8;
//
// Open EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL to access PCI config space.
// Note: This is done INSIDE the loop per the original binary behavior.
// The protocol GUID is {2F707EBB-4A1A-11D4-9A38-0090273FC14D}.
//
gBS->LocateProtocol (
&gEfiPciRootBridgeIoProtocolGuid,
NULL,
(VOID **)&PciRootBridgeIo
);
if (PciRootBridgeIo != 0) {
//
// Read PCI config space register at offset 0x19.
// On PCIe root ports, this is typically the Secondary Bus Number
// register. On standard PCI devices, this is BIST (Built-In Self Test).
// Intel chipsets repurpose this register to encode the start of
// the secondary bus range for this slot.
//
// The Pci.Read function at vtable index 7 (offset 0x38) is used.
// Signature:
// Pci.Read(This, Width=0 (Uint8), Address, Count, Buffer)
//
((VOID (*)(UINT64, UINT64, UINT64, UINT64, UINT8 *))
(*((UINT64 **)PciRootBridgeIo) + 7)) ( /* vtable[7] = offset 0x38 */
PciRootBridgeIo,
0, /* EfiPciWidthUint8 */
SlotBdfAddr | 0x19, /* Address: BDF | register 0x19 */
1, /* Count: 1 byte */
&RegValue19
);
//
// Read PCI config space register at offset 0x1A.
// This is the Subordinate Bus Number register on PCIe root ports,
// or Header Type on standard devices. Encodes the end of the
// secondary bus range for this slot.
//
((VOID (*)(UINT64, UINT64, UINT64, UINT64, UINT8 *))
(*((UINT64 **)PciRootBridgeIo) + 7)) (
PciRootBridgeIo,
0,
SlotBdfAddr | 0x1A, /* Address: BDF | register 0x1A */
1,
&RegValue1A
);
//
// Check if the target BDF address falls within the slot's range.
// If reg_0x19 <= PcieBdfAddr <= reg_0x1A, the device at this
// BDF is in the slot's secondary bus range.
//
if ((PcieBdfAddr >= RegValue19) && (PcieBdfAddr <= RegValue1A)) {
Found = TRUE;
}
}
}
}
return Found;
}
/**
* @brief Get pointer to Config Table A (primary PCIe topology).
*
* UBA framework callback registered in PBDS at offset +0x10.
* Returns a pointer to mConfigTableA (image addr 0xD20), which is
* a zero-initialized 64-byte buffer. On the LightningRidge EXEC B3
* platform, this table is unused - it acts as a terminator/null
* sentinel for the UBA framework.
*
* @param[out] ConfigTable Receives pointer to mConfigTableA.
*
* @return EFI_SUCCESS Always.
*/
EFI_STATUS
OpromGetConfigA (
OUT VOID **ConfigTable
)
{
*ConfigTable = (VOID *)&mConfigTableA;
return EFI_SUCCESS;
}
/**
* @brief Get pointer to Config Table B (6 PCIe root port entries).
*
* UBA framework callback registered in PBDS at offset +0x18.
* Returns pointer to mConfigTableB (image addr 0xD60) with 6 entries
* of 8 bytes each.
*
* The table encodes PCIe root port configuration for OpROM updates,
* including vendor/device IDs (Intel I350 - 0x8086:0x1528) and slot
* range information.
*
* @param[out] ConfigTable Receives pointer to mConfigTableB.
* @param[out] EntryCount Receives entry count (always 6).
*
* @return EFI_SUCCESS Always.
*/
EFI_STATUS
OpromGetConfigB (
OUT VOID **ConfigTable,
OUT UINTN *EntryCount
)
{
*ConfigTable = (VOID *)&mConfigTableB;
*EntryCount = 6;
return EFI_SUCCESS;
}
/**
* @brief Get pointer to Config Table C (10 extended PCIe entries).
*
* UBA framework callback registered in PBDS at offset +0x20.
* Returns pointer to mConfigTableC (image addr 0xE60) with 10 entries
* of 8 bytes each.
*
* The table contains extended device configuration for various Intel
* Ethernet controllers:
* - 0x8086:0x10D3 Intel 82599ES 10 Gigabit Ethernet
* - 0x8086:0x1521 Intel I350 Gigabit Ethernet
* - 0x8086:0x1522 Intel I350 Gigabit Ethernet (alternate)
* - 0x8086:0x1528 Intel I350 Gigabit Ethernet
* - 0x8086:0x10FB Intel 82599ES 10 Gigabit Ethernet (alternate)
*
* @param[out] ConfigTable Receives pointer to mConfigTableC.
* @param[out] EntryCount Receives entry count (always 10).
*
* @return EFI_SUCCESS Always.
*/
EFI_STATUS
OpromGetConfigC (
OUT VOID **ConfigTable,
OUT UINTN *EntryCount
)
{
*ConfigTable = (VOID *)&mConfigTableC;
*EntryCount = 10;
return EFI_SUCCESS;
}
/**
* @brief Set PCIe slot number callback.
*
* UBA framework callback registered in PBDS at offset +0x28.
* On the LightningRidge EXEC B3 platform, this function always sets
* the slot number to 0 and logs a debug message.
*
* The debug message: "[UBA]:SetPcieSlotNumber callback - %d\n"
* with argument 0, indicating that slot enumeration should start
* from slot number 0 for this platform.
*
* @param[out] SlotNumber Receives the slot number (always 0).
*
* @return EFI_SUCCESS Always.
*/
EFI_STATUS
OpromSetSlotNumber (
OUT UINT8 *SlotNumber
)
{
//
// Always set slot number to 0 on this platform.
//
*SlotNumber = 0;
//
// Log the slot assignment: "[UBA]:SetPcieSlotNumber callback - 0\n"
//
DebugPrint (0x80000000, "[UBA]:SetPcieSlotNumber callback - %d\n", 0);
return EFI_SUCCESS;
}
/*==============================================================================
* Module Summary
*
* == Binary Identity ==
* File: OpromUpdateDxeLightningRidgeEXECB3.efi
* Index: 31 of 427 PE files in HR650X BIOS
* Size: 4224 bytes (6 PE sections)
* SHA256: d58f84d1adb92017d59b450e242cac64939aadc6e24a35da7573064e7b531022
* PDB: OpromUpdateDxeLightningRidgeEXECB3.pdb
* PDB GUID: {2E401ABE-49E9-4BD3-969B-AF5A00349AA6}, Age 1
*
* == Source Path (from PDB) ==
* e:\hs\Build\HR6N0XMLK\DEBUG_VS2015\X64\PurleyRpPkg\Uba\UbaMain\Dxe\
* TypeLightningRidgeEXECB3\OpromUpdateDxe\OpromUpdateDxe\DEBUG\
* OpromUpdateDxeLightningRidgeEXECB3.pdb
*
* == Protocol GUIDs Used ==
* 0xCE0: {2F707EBB-4A1A-11D4-9A38-0090273FC14D} EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL
* 0xCF0: {36232936-0E76-31C8-A13A-3AF2FC1C3932} UBA_DEBUG_LIB_PROTOCOL
* 0xD00: {E03E0D46-5263-4845-B0A4-58D57B3177E2} UBA_CONFIG_PROTOCOL (EXECB3)
* 0xD10: {7739F24C-93D7-11D4-9A3A-0090273FC14D} EFI_HOB_LIST_GUID
* 0xEF0: {371BD79C-DE79-4C5F-AA2B-BC9EBEFA988F} OPROM_UPDATE_CONFIG_DATA
*
* == PCI Devices Identified in Config Tables ==
* 0x8086:0x1528 Intel I350 Gigabit Ethernet
* 0x8086:0x1521 Intel I350 GB Ethernet (alternate variant)
* 0x8086:0x1522 Intel I350 GB Ethernet (another variant)
* 0x8086:0x10D3 Intel 82599ES 10 Gigabit Ethernet
* 0x8086:0x10FB Intel 82599ES 10 Gigabit (alternate variant)
*
* == Compiled Library Paths (from ASSERT strings) ==
* e:\hs\MdePkg\Library\UefiBootServicesTableLib\UefiBootServicesTableLib.c
* e:\hs\MdePkg\Library\UefiRuntimeServicesTableLib\UefiRuntimeServicesTableLib.c
* e:\hs\MdePkg\Library\DxeHobLib\HobLib.c
* e:\hs\MdePkg\Library\BaseLib\Unaligned.c
*
* == Section Layout ==
* HEADER 0x000-0x2C0 PE headers and DOS stub
* .text 0x2C0-0x8DF Executable code (12 functions)
* .rdata 0x8E0-0xB3F Read-only data (strings, debug info)
* .data 0xB40-0xB73 Uninitialized globals (zeros)
* 0xB74-0xBFF PDB/RSDS debug record
* seg004 0xBE0-0xC3F Second PDB path string
* .xdata 0x1000-0x105F Exception handling data
* .reloc 0x1060-0x1073 Base relocations (6 entries for PBDS func ptrs)
*
* == Function Index ==
* 0x0390: _ModuleEntryPoint Entry Point (EFIAPI)
* 0x048C: IsPcieSlotConfigured PBDS callback [0] @+0x08
* 0x05B8: OpromGetConfigA PBDS callback [1] @+0x10
* 0x05C8: OpromGetConfigB PBDS callback [2] @+0x18
* 0x05DC: OpromGetConfigC PBDS callback [3] @+0x20
* 0x05F0: OpromSetSlotNumber PBDS callback [4] @+0x28
* 0x0614: GetDebugLibProtocol Helper (lazy protocol resolver)
* 0x0694: DebugPrint Helper (CMOS-based debug output)
* 0x071C: DebugAssert Helper (assert handler)
* 0x075C: GetHobList Helper (HOB list resolver)
* 0x0834: IsHobListGuid Helper (GUID comparator)
* 0x08A4: ReadUnaligned64 Helper (unaligned read)
*============================================================================*/