/**
* IioCfgUpdateDxeNeonCityFPGA.c
*
* IIO Configuration Update DXE Driver for the NeonCityFPGA platform.
*
* This driver is part of the Lenovo ThinkSystem HR650X UBA (Unified Board
* Architecture) framework. It registers platform-specific IIO (Integrated IO)
* configuration tables during the DXE phase.
*
* The IIO subsystem on Intel Xeon Scalable (Purley) platforms controls:
* - PCIe root port configuration and lane partitioning
* - IIO stack-to-port mappings
* - Link speed and width configurations
* - Bifurcation settings
*
* On the NeonCityFPGA platform, the IIO configuration is provided via
* four PIIO tables (PIIO = Platform IIO):
* - Table 0: 30 entries, entry size 228 bytes (GPIO/strapping config?)
* - Table 1: 27 entries, entry size 180 bytes
* - Table 2: 24 entries, entry size 156 bytes
* - Table 3: 27 entries, entry size 204 bytes
*
* Each PIIO table is identified by a unique GUID and registered with the
* UBA framework via the board-type protocol.
*
* Binary details:
* File: IioCfgUpdateDxeNeonCityFPGA.efi
* SHA256: 4ed7a6c35bc04e5dd3f7e59e6b388411b1dfcff0f2b3fabea6d37769bd0e16eb
* PE index: 0011
* .text: 0x2C0 - 0x7C0 (0x500 bytes, 9 functions)
* .rdata: 0x7C0 - 0xB80 (0x3C0 bytes, string literals)
* .data: 0xB80 - 0x1080 (0x500 bytes, GUIDs + PIIO tables)
* seg004: 0x1080 - 0x10E0 (0x60 bytes, unused/zeroed)
* .xdata: 0x10E0 - 0x1140 (0x60 bytes, zeroed)
* .reloc: 0x1140 - 0x1180 (0x40 bytes, base relocations)
* GAP: 0x1180 - 0x2000 (0xE80 bytes, BSS/alignment)
*/
#include "IioCfgUpdateDxeNeonCityFPGA.h"
/*===========================================================================
* GUID Const Data (.rdata section, embedded)
*===========================================================================*/
/*
* These GUIDs are embedded as const data in the .data section of the binary.
* They are declared here for clarity; the actual byte sequences match the
* GUID definitions in the header.
*/
/*===========================================================================
* Static Global Variables (.data section, 0xB80-0x1080)
*===========================================================================*/
/*
* Memory Layout of .data section:
*
* Offset Contents Size Description
* ------ -------- ---- -----------
* 0xB80 IIO_CFG_TABLE_0_GUID 16 GUID for table 0
* 0xB90 DEBUG_LIB_PROTOCOL_GUID 16 DebugLib protocol GUID
* 0xBA0 IIO_CFG_TABLE_1_GUID 16 GUID for table 1
* 0xBB0 UBA_BOARD_TYPE_PROTOCOL_GUID 16 UBA board-type protocol GUID
* 0xBC0 IIO_CFG_TABLE_2_GUID 16 GUID for table 2
* 0xBD0 EFI_HOB_LIST_GUID (first half) 16 1st 8 bytes: GUID, 2nd 8 bytes: padding
* Note: bytes at 0xBD0 match the upper 8 bytes of EFI_HOB_LIST_GUID
* and bytes at 0xBD8 match the lower 8 bytes -- they are stored
* split for the IsGuidEqual comparison.
* 0xBE0 IIO_CFG_TABLE_3_GUID 16 GUID for table 3
* + BoardConfigFlags (24) Platform config attribute bytes
* 0xC08 PIIO Table 0 header + descriptors (varies) Headers + 30 entry descriptors
* 0xDE8 PIIO Table 1 header (shared with table 2)
* 0xE18 PIIO Table 2 header
* 0xFF8 PIIO Table 3 header
* 0x1048 gST 8 EFI_SYSTEM_TABLE pointer
* 0x1050 gBS 8 EFI_BOOT_SERVICES pointer
* 0x1058 gImageHandle 8 EFI_HANDLE
* 0x1060 gRT 8 EFI_RUNTIME_SERVICES pointer
* 0x1068 mDebugProtocol 8 Cached DebugLib protocol pointer
* 0x1070 mHobList 8 Cached HOB list pointer
* 0x1078 mCmosDebugLevel 1 Cached CMOS debug level byte
*/
/*===========================================================================
* Static Variable Declarations (mapped at runtime)
*===========================================================================*/
/* UEFI boot service globals */
EFI_SYSTEM_TABLE *gST; /* Address: 0x1048 */
EFI_BOOT_SERVICES *gBS; /* Address: 0x1050 */
EFI_HANDLE gImageHandle;/* Address: 0x1058 */
EFI_RUNTIME_SERVICES *gRT; /* Address: 0x1060 */
/* Internal cached pointers */
static DEBUG_LIB_PROTOCOL *mDebugProtocol; /* Address: 0x1068 */
static VOID *mHobList; /* Address: 0x1070 */
static UINT8 mCmosDebugLevel;/* Address: 0x1078 */
/*===========================================================================
* PIIO Configuration Tables (embedded in .data section)
*===========================================================================*/
/*
* The 4 PIIO tables and their associated GUIDs are embedded as constant
* data in the binary at the following locations:
*
* GUID PIIO Header Table Description
* ---- ----------- -----------------
* 0xB80 0xC08 Table 0: 30 entries x 228 bytes
* 0xBA0 0xE18 Table 1: 24 entries x 156 bytes
* 0xBC0 0xDE8 Table 2: 27 entries x 180 bytes
* 0xBE0 0xFF8 Table 3: 27 entries x 204 bytes
*
* The GUIDs and headers are separated in memory but paired by the
* RegisterIioConfigTables() function.
*/
/*===========================================================================
* Helper Functions
*===========================================================================*/
/**
* Returns EFI_SUCCESS (thunk function).
*
* Present at 0x4F8 in the binary. Simply returns zero. No xrefs from
* within the module, suggesting this may be reachable via the PE
* export table or referenced by the linker.
*
* @return EFI_SUCCESS always.
*/
EFI_STATUS
ReturnSuccess(
VOID
)
{
return EFI_SUCCESS;
}
/**
* Reads a UINT64 from a pointer with NULL assertion.
*
* Implements the pattern from UEFI BaseLib's ReadUnaligned64().
* Located at 0x78C in the binary.
*
* @param[in] Buffer Pointer to read from. Must not be NULL.
* @return The 64-bit value at the given address.
*/
UINT64
ReadUnaligned64(
IN CONST UINT64 *Buffer
)
{
if (Buffer == NULL) {
DebugAssert("e:\\hs\\MdePkg\\Library\\BaseLib\\Unaligned.c", 192,
"Buffer != ((void *) 0)");
}
return *Buffer;
}
/**
* Compares two GUIDs using 64-bit unaligned reads.
*
* Optimized comparison that avoids calling CompareGuid(). Splits each
* GUID into two 64-bit halves and compares them directly. Used for
* scanning the system configuration table for the HOB list GUID.
*
* Note: The first GUID (a1) is compared against a config table entry
* at offset 0 (first 8 bytes are compared to unk_BD0, second 8 bytes
* to unk_BD8).
*
* Located at 0x71C in the binary.
*
* @param[in] Guid1 Pointer to the target GUID (from config table scan).
* @param[in] Guid2 Pointer to the reference GUID (EFI_HOB_LIST_GUID).
* @return TRUE if GUIDs match, FALSE otherwise.
*/
BOOLEAN
IsGuidEqual(
IN EFI_GUID *Guid1,
IN EFI_GUID *Guid2
)
{
UINT64 *Guid1Parts = (UINT64 *)Guid1;
UINT64 *Guid2Parts = (UINT64 *)Guid2;
/*
* Compare first 8 bytes of each GUID.
* Compares unk_BD0 (first half of EFI_HOB_LIST_GUID) against
* Guid2->Data1..Data2..Data3 (first 8 bytes of the config table entry).
*/
if (ReadUnaligned64((UINT64 *)&unk_BD0) != ReadUnaligned64(Guid2Parts)) {
return FALSE;
}
/*
* Compare second 8 bytes of each GUID.
* Compares unk_BD8 (second half of EFI_HOB_LIST_GUID) against
* Guid2->Data4 (bytes 8-15 of the config table entry).
*/
if (ReadUnaligned64((UINT64 *)&unk_BD8) != ReadUnaligned64(Guid2Parts + 1)) {
return FALSE;
}
return TRUE;
}
/**
* Resolves and caches the DebugLib protocol interface.
*
* Before calling LocateProtocol, performs a UEFI environment validation
* check by allocating and freeing a zero-byte buffer. If the returned
* pointer is <= 0x10, the boot services are considered non-functional
* and NULL is returned (the canary pattern detects a broken/missing
* UEFI environment).
*
* On success, caches the protocol pointer in mDebugProtocol and returns it.
*
* Located at 0x4FC in the binary.
*
* @return Pointer to DEBUG_LIB_PROTOCOL, or NULL on failure.
*/
DEBUG_LIB_PROTOCOL *
GetDebugProtocol(
VOID
)
{
DEBUG_LIB_PROTOCOL *Protocol;
VOID *Buffer;
EFI_STATUS Status;
/* Return cached pointer if already resolved */
if (mDebugProtocol != NULL) {
return mDebugProtocol;
}
/*
* Environment validation: allocate and free a zero-size buffer.
* On a functional UEFI, AllocatePool(31, 0, &Buffer) returns a
* valid pointer (not NULL, not <= 0x10). A broken or stubbed
* environment returns a very small or NULL pointer.
*/
Status = gBS->AllocatePool(31, 0, &Buffer);
if (!EFI_ERROR(Status)) {
gBS->FreePool(Buffer);
}
if ((UINTN)Buffer <= 0x10) {
return NULL;
}
/* Locate the DebugLib protocol */
Status = gBS->LocateProtocol(
&DEBUG_LIB_PROTOCOL_GUID,
NULL,
(VOID **)&Protocol
);
if (EFI_ERROR(Status)) {
Protocol = NULL;
}
mDebugProtocol = Protocol;
return Protocol;
}
/**
* Debug assertion handler.
*
* Calls the DebugLib protocol's DebugAssert function pointer at
* offset +0x08 of the protocol interface. If the DebugLib protocol
* is not available, silently ignores the assertion.
*
* Located at 0x604 in the binary.
*
* @param[in] FileName Source file name where assertion occurred.
* @param[in] LineNumber Line number of the assertion.
* @param[in] Description Assertion condition description string.
*/
VOID
DebugAssert(
IN CONST CHAR8 *FileName,
IN UINT64 LineNumber,
IN CONST CHAR8 *Description
)
{
DEBUG_LIB_PROTOCOL *Protocol;
Protocol = GetDebugProtocol();
if (Protocol != NULL) {
Protocol->DebugAssert(FileName, LineNumber, Description);
}
}
/**
* Debug print function with CMOS-level verbosity filtering.
*
* Behavior:
* 1. Resolves the DebugLib protocol if not already cached.
* 2. Reads CMOS register 0x4B via RTC ports 0x70/0x71 to get
* the board-specific debug level.
* 3. If CMOS level > 3, clamps to the raw value.
* If CMOS level == 0, falls back to MMIO 0xFDAF0490 to read
* the hardware debug level.
* 4. Maps debug level to an ErrorLevel filter mask:
* Level 1 -> ErrorLevel = 0x80000004 (EFI_D_ERROR | ...)
* Otherwise -> ErrorLevel = 0x80000006
* 5. Calls the DebugLib DebugPrint if the message's ErrorLevel
* passes the filter.
*
* Located at 0x57C in the binary.
*
* @param[in] ErrorLevel Debug message error level.
* @param[in] Format Format string.
* @param[in] ... Variable arguments.
* @return 1 if message was printed, 0 if filtered or no protocol.
*/
UINT8
DebugPrint(
IN UINT64 ErrorLevel,
IN CONST CHAR8 *Format,
...
)
{
DEBUG_LIB_PROTOCOL *Protocol;
UINT8 DebugLevel;
UINT8 CmosIndex;
UINT64 FilterMask;
VA_LIST Args;
Protocol = GetDebugProtocol();
if (Protocol == NULL) {
return 0;
}
/* Read CMOS register 0x4B for debug level */
__outbyte(0x70, (__inbyte(0x70) & 0x80) | 0x4B); /* Preserve NMI bit */
DebugLevel = __inbyte(0x71);
/*
* Normalize debug level:
* - If Level > 3, keep as-is (possibly a special encoding)
* - If Level == 0, read board-level debug from MMIO 0xFDAF0490
* (a board-specific register on NeonCityFPGA)
*/
if (DebugLevel > 3) {
/* Keep raw value */
} else if (DebugLevel == 0) {
/*
* Fallback: read MMIO register at 0xFDAF0490.
* This register provides the board's debug configuration.
* Only the low 2 bits are used: (value & 2) | 1
* gives a valid debug level of 1 or 3.
*/
DebugLevel = (*(volatile UINT8 *)0xFDAF0490 & 2) | 1;
}
/*
* Map debug level to filter mask:
* Level 1 -> 0x80000004 (EFI_D_ERROR = 0x80000000 | 0x4)
* Other -> 0x80000006 (EFI_D_ERROR | EFI_D_WARN = 0x80000000 | 0x6)
*
* Note: 0x80000000 is the high bit set for error-level messages.
* The lower bits correspond to UEFI debug level masks:
* 0x00000001 = EFI_D_INIT
* 0x00000002 = EFI_D_WARN
* 0x00000004 = EFI_D_ERROR
*/
if (DebugLevel == 1) {
FilterMask = 0x80000004;
} else {
FilterMask = 0x80000006;
}
/* Check if the message passes the filter */
if ((FilterMask & ErrorLevel) != 0) {
VA_START(Args, Format);
Protocol->DebugPrint(ErrorLevel, Format, Args);
VA_END(Args);
return 1;
}
return 0;
}
/**
* Locates and caches the HOB (Hand-Off Block) list pointer.
*
* Scans the SystemTable->ConfigurationTable[] array for the
* EFI_HOB_LIST_GUID entry. Each configuration table entry is
* 0x18 (24) bytes:
* +0x00: EFI_GUID VendorGuid (16 bytes)
* +0x10: VOID *VendorTable (8 bytes)
*
* The number of entries is at SystemTable->NumberOfTableEntries
* (offset +0x68 from SystemTable base).
* The ConfigurationTable pointer is at SystemTable + 0x70.
*
* On success, stores the HOB list pointer in mHobList and returns it.
* On failure (no matching entry), triggers an assertion with
* EFI_NOT_FOUND status code (0x800000000000000E).
*
* Located at 0x644 in the binary.
*
* @param[in] SystemTable Pointer to EFI_SYSTEM_TABLE (not directly used;
* the function reads from the global gST instead).
* @return Pointer to the HOB list, or NULL if not found.
*/
VOID *
GetHobList(
IN EFI_SYSTEM_TABLE *SystemTable /* UNUSED - uses gST global */
)
{
UINTN Index;
UINT64 EntryCount;
UINT8 *ConfigTableBase;
EFI_GUID *EntryGuid;
if (mHobList != NULL) {
return mHobList;
}
mHobList = NULL;
/* Get number of configuration table entries from SystemTable+0x68 */
EntryCount = *(UINT64 *)((UINT8 *)gST + 0x68);
if (EntryCount == 0) {
goto Error_NotFound;
}
/* Get ConfigurationTable pointer from SystemTable+0x70 */
ConfigTableBase = *(UINT8 **)((UINT8 *)gST + 0x70);
/* Scan for EFI_HOB_LIST_GUID */
for (Index = 0; Index < EntryCount; Index++) {
EntryGuid = (EFI_GUID *)(ConfigTableBase + (Index * 24));
if (IsGuidEqual(NULL, EntryGuid)) {
/*
* Found matching entry.
* The HOB list pointer is at offset +0x10 within the
* configuration table entry.
*/
mHobList = *(VOID **)(ConfigTableBase + (Index * 24) + 16);
break;
}
}
if (mHobList == NULL) {
goto Error_NotFound;
}
return mHobList;
Error_NotFound:
DebugPrint(0x80000000, "\nASSERT_EFI_ERROR (Status = %r)\n", EFI_NOT_FOUND);
DebugAssert("e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c", 54,
"!EFI_ERROR (Status)");
DebugAssert("e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c", 55,
"mHobList != ((void *) 0)");
return mHobList;
}
/**
* Registers all IIO configuration tables with the UBA framework.
*
* This function performs the main work of the driver:
* 1. Prints the "UBA:IioCfgUpdate-TypeNeonCityFPGA" banner via DebugPrint.
* 2. Locates the UBA board-type protocol via gBS->LocateProtocol().
* 3. Calls RegisterIioCfg() for each of the 4 IIO configuration tables,
* passing the table GUID, PIIO header, and a size of 48 bytes.
*
* Each table registration passes exactly the PIIO_TABLE_HEADER (48 bytes)
* as the configuration data. The UBA framework uses the header to locate
* and interpret the entry descriptors that follow in memory.
*
* Located at 0x43C in the binary.
*
* @return EFI_SUCCESS on success, or the error status from LocateProtocol.
*/
EFI_STATUS
RegisterIioConfigTables(
VOID
)
{
EFI_STATUS Status;
UBA_IIO_CFG_PROTOCOL *UbaProtocol = NULL;
/* Print initialization banner */
DebugPrint(0x80000000, "UBA:IioCfgUpdate-TypeNeonCityFPGA\n");
/* Locate the UBA board-type protocol */
Status = gBS->LocateProtocol(
&UBA_BOARD_TYPE_PROTOCOL_GUID,
NULL,
(VOID **)&UbaProtocol
);
if (EFI_ERROR(Status)) {
return Status;
}
/*
* Register all 4 IIO configuration tables.
* Each call passes:
* - The 16-byte GUID identifying the config table
* - The 48-byte PIIO_TABLE_HEADER describing the table layout
* - Size = 48 (the PIIO header size; the full table including
* entry descriptors extends beyond this in memory but is
* accessed through the TotalSize/DataOffset fields in the header)
*/
/* Table 0: GUID at 0xB80, PIIO header at 0xC08, 30 entries, esize=228 */
UbaProtocol->RegisterIioCfg(
UbaProtocol,
(EFI_GUID *)&IIO_CFG_TABLE_0_GUID,
(PIIO_TABLE_HEADER *)0xC08,
48
);
/* Table 1: GUID at 0xBA0, PIIO header at 0xE18, 24 entries, esize=156 */
UbaProtocol->RegisterIioCfg(
UbaProtocol,
(EFI_GUID *)&IIO_CFG_TABLE_1_GUID,
(PIIO_TABLE_HEADER *)0xE18,
48
);
/* Table 2: GUID at 0xBC0, PIIO header at 0xDE8, 27 entries, esize=180 */
UbaProtocol->RegisterIioCfg(
UbaProtocol,
(EFI_GUID *)&IIO_CFG_TABLE_2_GUID,
(PIIO_TABLE_HEADER *)0xDE8,
48
);
/* Table 3: GUID at 0xBE0, PIIO header at 0xFF8, 27 entries, esize=204 */
UbaProtocol->RegisterIioCfg(
UbaProtocol,
(EFI_GUID *)&IIO_CFG_TABLE_3_GUID,
(PIIO_TABLE_HEADER *)0xFF8,
48
);
return EFI_SUCCESS;
}
/*===========================================================================
* Module Entry Point
*===========================================================================*/
/**
* Module entry point.
*
* Called by the DXE dispatcher during driver initialization. Performs:
* 1. Caches gImageHandle (asserts if NULL).
* 2. Caches gST (asserts if NULL).
* 3. Extracts and caches gBS from SystemTable->BootServices (asserts if NULL).
* 4. Extracts and caches gRT from SystemTable->RuntimeServices (asserts if NULL).
* 5. Calls GetHobList() to locate and cache the HOB list pointer.
* 6. Calls RegisterIioConfigTables() to register all 4 PIIO tables.
*
* Located at 0x390 in the binary.
*
* @param[in] ImageHandle The firmware allocated handle for the EFI image.
* @param[in] SystemTable A pointer to the EFI System Table.
* @return The result of RegisterIioConfigTables().
*/
EFI_STATUS
EFIAPI
ModuleEntryPoint(
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
/* Validate and cache ImageHandle */
gImageHandle = (UINT64)ImageHandle;
if (ImageHandle == NULL) {
DebugAssert("e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\"
"UefiBootServicesTableLib.c", 51,
"gImageHandle != ((void *) 0)");
}
/* Validate and cache SystemTable */
gST = (UINT64)SystemTable;
if (SystemTable == NULL) {
DebugAssert("e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\"
"UefiBootServicesTableLib.c", 57,
"gST != ((void *) 0)");
}
/* Extract and cache BootServices from SystemTable->BootServices (+0x60) */
gBS = (UINT64)SystemTable->BootServices;
if (gBS == NULL) {
DebugAssert("e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\"
"UefiBootServicesTableLib.c", 63,
"gBS != ((void *) 0)");
}
/* Extract and cache RuntimeServices from SystemTable->RuntimeServices (+0x58) */
gRT = (UINT64)SystemTable->RuntimeServices;
if (gRT == NULL) {
DebugAssert("e:\\hs\\MdePkg\\Library\\UefiRuntimeServicesTableLib\\"
"UefiRuntimeServicesTableLib.c", 47,
"gRT != ((void *) 0)");
}
/* Locate and cache the HOB list */
GetHobList(SystemTable);
/* Register all IIO configuration tables */
return RegisterIioConfigTables();
}