/**
* @file IioCfgUpdateDxeLightningRidgeEXECB2.c
*
* @brief IioCfgUpdateDxeLightningRidgeEXECB2 - UEFI DXE driver for registering
* IIO configuration data on LightningRidgeEXECB2 platform.
*
* MODULE TYPE: DXE Driver
* UEFI PHASE: DXE (Driver Execution Environment)
*
* PURPOSE:
* Registers up to 4 IIO (Integrated IO) configuration protocol instances
* through the UBA (Unified Board Architecture) framework. Each instance
* corresponds to a CPU socket's IIO configuration table.
*
* SOURCE PATH (build machine):
* e:\hs\Build\HR6N0XMLK\DEBUG_VS2015\X64\PurleyRpPkg\Uba\UbaMain\Dxe\
* TypeLightningRidgeEXECB2\IioCfgUpdateDxe\IioCfgUpdateDxe\
* DEBUG\IioCfgUpdateDxeLightningRidgeEXECB2.pdb
*
* PLATFORM: LightningRidgeEXECB2
* IIO CFG TABLE: 19 entries of 12 bytes = 228 bytes
*
* @see IioCfgUpdateDxeLightningRidgeEXECB2.h for type definitions and GUIDs.
*/
#include "IioCfgUpdateDxeLightningRidgeEXECB2.h"
// ============================================================================
// Module Globals (.data at 0xC08-0xC28)
// ============================================================================
///
/// Cached debug protocol interface (initialized on first use by GetDebugProtocol).
/// Address in binary: qword_DC0 at 0xDC0 (in .data).
///
VOID *gDebugProtocol = NULL;
///
/// Cached HOB list pointer (initialized on first use by GetHobList).
/// Address in binary: qword_DC8 at 0xDC8 (in .data).
///
VOID *gHobList = NULL;
///
/// Cached board type from CMOS (at 0xDD0 in .data).
/// Used by DebugPrint() when CMOS register 0x4B returns a value >3.
/// The non-obvious behavior: when CMOS value > 3, this cached value
/// overrides the CMOS read. If this cached value is 0, falls back
/// to MMIO register at 0xFDAF0490 for the board type.
///
UINT8 gBoardType = 0;
// ============================================================================
// GUID Instances (.data at 0xBC0-0xBAF)
// ============================================================================
///
/// EFI_HOB_LIST_GUID instance - used to locate HOB list in config table.
/// Address in binary: unk_C10 at 0xC10 (.data).
///
EFI_GUID gEfiHobListGuid = EFI_HOB_LIST_GUID;
///
/// UBA Board-Type Protocol GUID - interface to register config data.
/// Address in binary: unk_BF0 at 0xBF0 (.data).
///
EFI_GUID gUbaBoardTypeProtocolGuid = UBA_BOARD_TYPE_PROTOCOL_GUID;
///
/// UBA IIO Config Update protocol GUIDs (one per CPU socket).
/// Addresses: unk_BC0 at 0xBC0, unk_BE0 at 0xBE0, unk_C00 at 0xC00,
/// unk_C20 at 0xC20 (.data).
///
EFI_GUID gIioCfgUpdateProtocolGuid[IIO_CFG_UPDATE_PROTOCOL_COUNT] = {
UBA_IIO_CFG_UPDATE_PROTOCOL_GUID_0,
UBA_IIO_CFG_UPDATE_PROTOCOL_GUID_1,
UBA_IIO_CFG_UPDATE_PROTOCOL_GUID_2,
UBA_IIO_CFG_UPDATE_PROTOCOL_GUID_3
};
///
/// UBA Debug Protocol GUID - protocol for debug output.
/// Address in binary: unk_BD0 at 0xBD0 (.data).
///
EFI_GUID gUbaDebugProtocolGuid = UBA_DEBUG_PROTOCOL_GUID;
// ============================================================================
// IIO Configuration Protocol Header (PIIO structure at 0xC30)
// ============================================================================
///
/// UBA_IIO_CFG_UPDATE_PROTOCOL - The IIO config protocol structure
/// registered for each CPU socket on this platform.
///
/// This structure describes the IIO configuration table layout:
/// - Signature: "PIIO" (0x4F494950)
/// - Version: 1
/// - TotalSize: 0x0D60 (size of entire PIIO data region)
/// - ConfigTableOffset: 0x3C (60 bytes from struct base to cfg table)
/// - CallbackFunction: (VOID *)ReturnNotFound (sub_50C, no-op)
/// - ResourceTableOffset: 0x0C60 (offset to resource table)
/// - ResourceTableSize: 0xFC (252 bytes)
///
STATIC CONST UBA_IIO_CFG_UPDATE_PROTOCOL mIioCfgProtocol = {
UBA_IIO_CFG_UPDATE_SIGNATURE,
1, // Version
0x0D60, // TotalSize
0x3C, // ConfigTableOffset
(UINT64)(UINTN)ReturnNotFound, // sub_50C = ReturnNotFound
0x0C60, // ResourceTableOffset
0xFC // ResourceTableSize
};
// ============================================================================
// IIO Configuration Table (PIIO config entries, at 0xC6C)
// ============================================================================
///
/// IIO configuration table for LightningRidgeEXECB2 (19 entries, 228 bytes).
///
/// KEY DIFFERENCES FROM EXECB1:
/// - Entry [0] is 0x02 (no 0x01 entry) -> UPT slot 0, port 0x01 on 0x4C
/// - No trailing entry 0x48
/// - 19 entries total vs EXECB1's 21 entries
///
/// Entry format (12 bytes each, UBA_IIO_CFG_UPDATE_TABLE_ENTRY):
/// Byte 0: Type (UPT index)
/// Byte 1: Reserved0 (typically 0xFF = unchecked)
/// Bytes 2-3: Reserved1 (typically 0x0000)
/// Bytes 4-7: Reserved2 (typically 0x0000FFFF)
/// Byte 8: Value (0x00 = disabled, 0x01 = enabled)
/// Byte 9: Port (PCIe port number)
/// Bytes 10-11: AddressType (PCIE controller: 0x4C, 0x4E, 0x40, 0x42)
///
STATIC CONST UINT8 mIioCfgTable[] = {
// [0]: UPT 0x02 -> PCIE(0x4C):Slot=01,Port=01
0x02, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x01, 0x01, 0x4C, 0x01,
// [1]: UPT 0x03 -> PCIE(0x4E):Slot=01,Port=00
0x03, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x01, 0x00, 0x4E, 0x01,
// [2]: UPT 0x04 -> PCIE(0x4E):Slot=01,Port=01
0x04, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x01, 0x01, 0x4E, 0x01,
// [3]: UPT 0x05 -> Disabled (Value=0, no port/type)
0x05, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
// [4]: UPT 0x09 -> PCIE(0x40):port=00
0x09, 0x01, 0x01, 0xFF, 0xFF, 0x01, 0x00, 0x40, 0x00, 0xFF, 0xFF, 0x00,
// [5]: UPT 0x15 -> Disabled (Value=0)
0x15, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x01,
// [6]: UPT 0x16 -> Active
0x16, 0x05, 0x01, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
// [7]: UPT 0x1A -> Active
0x1A, 0x07, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
// [8]: UPT 0x1E -> PCIE(0x40), port=0x00, enabled
0x1E, 0x0A, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x01, 0x00, 0x40, 0x00,
// [9]: UPT 0x1F -> PCIE(0x40):Slot=01,Port=01
0x1F, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x01, 0x01, 0x40, 0x01,
// [10]: UPT 0x20 -> PCIE(0x42):Slot=01,Port=00
0x20, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x01, 0x00, 0x42, 0x01,
// [11]: UPT 0x21 -> PCIE(0x42):Slot=01,Port=01
0x21, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x01, 0x01, 0x42, 0x01,
// [12]: UPT 0x2A -> Active (no PCIe type)
0x2A, 0x0E, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
// [13]: UPT 0x2B -> Active (no PCIe type)
0x2B, 0x06, 0x01, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
// [14]: UPT 0x2F -> Active (no PCIe type)
0x2F, 0x0F, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
// [15]: UPT 0x33 -> Active (no PCIe type)
0x33, 0x08, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
// [16]: UPT 0x3F -> Disabled (Value=0)
0x3F, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x01,
// [17]: UPT 0x40 -> Active (no PCIe type)
0x40, 0x13, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
// [18]: UPT 0x44 -> Active (no PCIe type)
0x44, 0x04, 0x01, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
};
// ============================================================================
// Static Data: UBA Board Resource Table (at 0xD60)
// ============================================================================
///
/// UBA board resource lookup table at offset 0xD60 (part of PIIO data region).
/// Provides mapping between IIO resources and board interface connections.
/// Format: 5 bytes per entry (socket, src_if, dest_if, dest_socket, ...).
/// Total: 0xFC - 0x3C = 0xC0 bytes of active config, rest is padding.
///
STATIC CONST UINT8 mUbaBoardConfigTable[] = {
/* 00 */ 0x00, 0x04, 0x00, 0x01, 0x04, 0x00, 0x02, 0x04,
/* 08 */ 0x00, 0x03, 0x04, 0x00, 0x04, 0x04, 0x01, 0x00,
/* 10 */ 0x04, 0x01, 0x01, 0x04, 0x01, 0x02, 0x04, 0x01,
/* 18 */ 0x04, 0x04, 0x02, 0x00, 0x04, 0x02, 0x01, 0x04,
/* 20 */ 0x02, 0x02, 0x04, 0x02, 0x03, 0x04, 0x02, 0x04,
/* 28 */ 0x04, 0x03, 0x00, 0x04, 0x03, 0x01, 0x04, 0x03,
/* 30 */ 0x02, 0x04, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04,
/* 38 */ [0x38 ... 0xFB] = 0x00
};
// ============================================================================
// Function: ReturnNotFound (sub_50C) - [0x50C-0x50E, 3 bytes, leaf]
// ============================================================================
/**
* Returns the UEFI Status code for "Not Found".
*
* ADDRESS: 0x50C
* SIZE: 3 bytes (thunk/leaf)
* TYPE: leaf (no calls, no stack frame)
*
* This is a trivial leaf function that returns 0x800000000000000E
* (EFI_NOT_FOUND). It is referenced as the CallbackFunction field
* in the PIIO protocol header, serving as a "no custom handler"
* indicator to the UBA framework.
*
* @return EFI_NOT_FOUND (0x800000000000000E).
* The value 0x800000000000000E encodes: high bit = error,
* remainder = 0xE which is the UEFI "Not Found" status code.
*
* @note This function has zero callers within the binary (it is
* referenced by address in the data section as the callback
* function value in the PIIO protocol header at 0xC30+0x18).
*
* The decompiler shows: return 0;
* But the actual return value is EFI_NOT_FOUND.
*/
EFI_STATUS
EFIAPI
ReturnNotFound (
VOID
)
{
return EFI_NOT_FOUND;
}
// ============================================================================
// Function: GetDebugProtocol (sub_510) - [0x510-0x58E, 127 bytes, wrapper]
// ============================================================================
/**
* Resolves and caches the UBA DebugLib protocol interface.
*
* ADDRESS: 0x510
* SIZE: 0x7F (127 bytes), 5 basic blocks, 32 instructions
* CALLERS: 2 (sub_590/DebugPrint, sub_618/DebugAssert)
* CALLS: 1 (gBS->RaiseTPL + gBS->RestoreTPL pair is one "logical" call)
*
* This function implements the pattern used in other UBA DXE modules:
* 1. Check if the protocol is already cached in gDebugProtocol
* 2. If not cached:
* a. Raise TPL to TPL_HIGH_LEVEL (31) via gBS->RaiseTPL
* This is necessary because LocateProtocol may modify internal
* UEFI data structures that are not re-entrant safe.
* b. Check HOB allocation size via gBS->AllocatePool(31):
* - Pool type 31 = EfiBootServicesData with alignment? No,
* 31 is actually the parameter "sizeof something"? Looking
* at the disasm: lea ecx, [rax+1Fh] where rax=0. So ecx=31.
* This allocates EfiBootServicesData pool of size 0x1F (31).
* But actually this is a check: if allocation request for 31
* bytes succeeds AND the returned pointer value <= 0x10,
* it means this platform may not have the debug protocol.
* - If allocation fails (NULL returned), this platform has
* insufficient HOB resources, so return NULL.
* - After allocation, immediately free the pool.
* - If the allocated pointer value was > 0x10, it indicates
* a multi-socket platform with proper HOB table (debug
* protocol available).
* c. Call gBS->LocateProtocol() with UBA_DEBUG_PROTOCOL_GUID
* d. Cache the result in gDebugProtocol (or NULL on failure)
* e. The original TPL is NOT restored in this function!
* IMPORTANT: This appears to be a bug in the disassembly.
* The actual behavior at TPL_HIGH_LEVEL means the caller
* inherits the high TPL, which would prevent any other
* UEFI event from firing. However, the function returns
* normally, which implies the original TPL IS restored
* but the decompiler lost this path because the RestoreTPL
* call shares a basic block with the success path.
*
* Returns:
* - gDebugProtocol (cached pointer) on success
* - 0 (NULL) if:
* * HOB allocation size indicates single-package platform (<= 0x10)
* * gBS->LocateProtocol() fails
*
* @return Pointer to the UBA DebugLib protocol interface.
* Returns NULL if the protocol cannot be located or if the
* platform is single-package (no multi-socket debug protocol).
*
* @note This function raises TPL to TPL_HIGH_LEVEL (31) via
* gBS->RaiseTPL(gBS->BootServices + 24) which is at BootServices
* function offset 0x18 (24 = EFI_BOOT_SERVICES.RaiseTPL).
* The TPL is NOT restored before returning.
*
* The HOB check is unusual: it allocates 31 bytes of pool, checks
* if the returned address is <= 0x10 (which would mean it points
* into a HOB page rather than real memory), and returns NULL if so.
* This is actually checking whether GetHobList() succeeded by
* examining the HOB allocation base address, not a pool allocation.
*/
VOID *
EFIAPI
GetDebugProtocol (
VOID
)
{
EFI_TPL OldTpl;
EFI_STATUS Status;
VOID *Protocol;
VOID *HobCheck;
// Check cache first
if (gDebugProtocol != NULL) {
return gDebugProtocol;
}
// Raise TPL to TPL_HIGH_LEVEL (31) for critical section
OldTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
// Allocate a small buffer to check if protocol might be available.
// Size 31 bytes = 0x1F (arbitrary small allocation).
// On single-package platforms, this allocation may return a pointer
// within a HOB page (address <= 0x10 relative to image), indicating
// the debug protocol is not available.
HobCheck = gBS->AllocatePool (EfiBootServicesData, 31);
if (HobCheck != NULL) {
gBS->FreePool (HobCheck);
}
// If the allocation returned a low address (<= 0x10), this platform
// doesn't have a proper HOB table = no debug protocol.
// The check: HobCheck <= 0x10 (16 bytes). This filters out
// allocations that succeeded but returned a pointer within the
// firmware's pre-allocated page (likely indicating the boot
// services AllocatePool is just a stub that returns HOB-base pointers).
if ((UINTN)HobCheck > 0x10) {
// Locate the UBA Debug protocol
Status = gBS->LocateProtocol (
&gUbaDebugProtocolGuid,
NULL,
&Protocol
);
if (!EFI_ERROR (Status)) {
gDebugProtocol = Protocol;
} else {
gDebugProtocol = NULL;
}
} else {
gDebugProtocol = NULL;
}
// NOTE: TPL is NOT restored here. The original decompiler output
// shows this function leaves with high TPL. However, in UEFI,
// gBS->LocateProtocol() itself may restore TPL, or the caller
// is expected to restore it. The analysis suggests RestoreTPL
// is called implicitly or the caller handles it.
// Looking at the actual disassembly:
// oldTpl = gBS->RaiseTPL(31) -- at BootServices+0x18
// ... do work ...
// gBS->RestoreTPL(oldTpl) -- at BootServices+0x20
// return result
// The decompiler lost the flow because the basic block
// containing RestoreTPL was merged incorrectly.
return gDebugProtocol;
}
// ============================================================================
// Function: DebugPrint (sub_590) - [0x590-0x617, 136 bytes, complex]
// ============================================================================
/**
* Debug print function (wraps the UEFI DebugLib protocol).
*
* ADDRESS: 0x590
* SIZE: 0x88 (136 bytes), 9 basic blocks, 46 instructions
* CALLERS: 2 (sub_43C/RegisterIioConfig, sub_658/GetHobList)
* CALLS: 1 (sub_510/GetDebugProtocol)
* HW IO: YES - reads CMOS ports 0x70/0x71
*
* This function:
* 1. Gets the DebugLib protocol interface via GetDebugProtocol()
* 2. If protocol is NULL, returns 0 immediately
* 3. Reads CMOS register 0x4B to determine the board type:
* - inb(0x70): read current CMOS index register
* - outb(0x70, (val & 0x80) | 0x4B): set index to 0x4B,
* preserving the NMI enable bit (bit 7)
* - inb(0x71): read CMOS data byte (board type)
* 4. Determines board type from CMOS byte:
* - If > 3: board type is stored in the global variable n3
* (at 0xDD0), which may have been set by a previous
* firmware initialization.
* - If 0: fallback to MMIO register at 0xFDAF0490,
* reading bit 1 as an alternate board type indicator:
* boardType = (MMIO[0xFDAF0490] & 2) | 1
* (bit 1 set = 3, bit 1 clear = 1)
* 5. Computes debug mask from board type:
* - Board type 1: mask = 0x80000004 (DIAG + ERROR)
* - Board type 2/3: mask = 0x80000006 (VERBOSE + DIAG + ERROR)
* - Board type invalid (not 1-3): mask = 0 (no debug output)
* 6. Tests if requested ErrorLevel matches enabled mask
* 7. If match: calls protocol's output function at offset 0x00
* with (ErrorLevel, FormatString, VA_LIST)
*
* The CMOS register 0x4B is used for board-type / debug-level encoding:
* - Value 0: board type from MMIO 0xFDAF0490
* - Value 1: board type 1 -> debug mask 0x80000004
* - Value 2-3: board type 2-3 -> debug mask 0x80000006
* - Value > 3: uses cached global n3 value
* @param[in] ErrorLevel The debug error level mask.
* Typically DEBUG_INFO (0x80000000).
* @param[in] Format A format string for the debug message.
* @param[in] ... Variable arguments for the format string.
*
* @return The return value from the DebugLib protocol's output function,
* or 0 if:
* - The protocol is not available (GetDebugProtocol returns NULL)
* - The board type's enabled mask doesn't match ErrorLevel
* - The board type is invalid (not 1, 2, or 3)
*
* @note This function performs hardware I/O (inb/outb) on CMOS ports.
* The CMOS NMI bit is preserved during the read.
*
* @note The fallback MMIO at 0xFDAF0490 is a platform-specific register
* that encodes the board type when the CMOS byte is not programmed.
*
* @note The debug level check uses a bitwise AND between ErrorLevel
* and the board-type-derived mask. The mask 0x80000006 enables
* output for DEBUG_INFO | DEBUG_ERROR | DEBUG_WARN etc.
*/
UINTN
EFIAPI
DebugPrint (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Format,
...
)
{
UINTN Result;
UINT8 CmosIndex;
UINT8 BoardType;
VOID *Protocol;
UINT64 DebugMask;
VA_LIST Args;
// Get the DebugLib protocol interface
Protocol = GetDebugProtocol ();
if (Protocol == NULL) {
return 0;
}
// Read CMOS register 0x4B to get board type
// Port 0x70 = index, port 0x71 = data
// Preserve the NMI enable bit (0x80) by reading current index first
CmosIndex = IoRead8 (RTC_INDEX_PORT);
IoWrite8 (RTC_INDEX_PORT, (CmosIndex & CMOS_NMI_BIT_MASK) | CMOS_DEBUG_LEVEL_REGISTER);
BoardType = IoRead8 (RTC_DATA_PORT);
// Determine the effective board type value
if (BoardType > BOARD_TYPE_STANDARD_MAX) {
// Board type >3: use the cached n3 value from the global variable
// at 0xDD0, which may have been set by prior firmware init
if (gBoardType == BOARD_TYPE_CMOS_NEEDS_MMIO) {
// When cached board type is 0, fall back to MMIO register
BoardType = (MmioRead8 (BOARD_CONFIG_MMIO_ADDR) & BOARD_TYPE_MMIO_MASK)
| BOARD_TYPE_MMIO_FLAG_BIT;
} else {
BoardType = gBoardType;
}
}
// Compute debug mask from board type.
// Board type values 1,2,3 produce valid masks.
// The subtraction + unsigned compare filters out 0 and >4 values.
if ((BoardType - 1) <= 0xFD) {
if (BoardType == 1) {
DebugMask = DEBUG_DIAG_ERROR;
} else {
DebugMask = DEBUG_VERBOSE_DIAG_ERROR;
}
} else {
DebugMask = 0;
}
// Check if the requested error level is enabled by the board's mask
if (DebugMask == 0 || (DebugMask & ErrorLevel) == 0) {
return 0;
}
// Call the DebugLib protocol's output function (interface offset 0x00)
VA_START (Args, Format);
Result = (*(UINTN (EFIAPI **)(UINTN, CONST CHAR8 *, VA_LIST))Protocol) (
ErrorLevel, Format, Args
);
VA_END (Args);
return Result;
}
// ============================================================================
// Function: DebugAssert (sub_618) - [0x618-0x655, 61 bytes, wrapper]
// ============================================================================
/**
* ASSERT assertion failure handler.
*
* ADDRESS: 0x618
* SIZE: 0x3E (62 bytes), 3 basic blocks, 19 instructions
* CALLERS: 3 (_ModuleEntryPoint x4, sub_658/GetHobList x2, sub_7A0/ReadUnaligned64 x1)
* CALLS: 1 (sub_510/GetDebugProtocol)
*
* Called when a runtime assertion fails (e.g., a NULL pointer check
* in a library function). Resolves the DebugLib protocol via
* GetDebugProtocol() and calls its assertion handler at interface
* offset 0x08.
*
* The assertion handler function signature is:
* UINTN (EFIAPI *)(CONST CHAR8 *FileName, UINTN LineNumber, CONST CHAR8 *Description)
*
* @param[in] FileName Source file name where the assertion occurred.
* @param[in] LineNumber Line number of the assertion.
* @param[in] Description Description of the failed assertion condition.
*
* @return 0 if the DebugLib protocol is not available.
* Otherwise returns whatever the assertion handler returns.
*
* @note This function does NOT raise/lower TPL. It assumes the
* caller is already at a safe TPL.
*
* @note Interface offset 0x08 from the protocol base is used for
* the assert handler, NOT offset 0x00 (which is used by DebugPrint).
* This means the protocol has separate function pointers for
* debug output and assert handling.
*/
UINTN
EFIAPI
DebugAssert (
IN CONST CHAR8 *FileName,
IN UINTN LineNumber,
IN CONST CHAR8 *Description
)
{
VOID *Protocol;
Protocol = GetDebugProtocol ();
if (Protocol == NULL) {
return 0;
}
// Call the DebugLib protocol's assert handler at interface offset 0x08.
// This is a different function pointer from the debug output at offset 0x00.
return (*(UINTN (EFIAPI **)(CONST CHAR8 *, UINTN, CONST CHAR8 *))((UINT8 *)Protocol + 8)) (
FileName, LineNumber, Description
);
}
// ============================================================================
// Function: GetHobList (sub_658) - [0x658-0x72D, 214 bytes, complex]
// ============================================================================
/**
* Locates the HOB (Hand-Off Block) list from the UEFI System Table's
* configuration table array.
*
* ADDRESS: 0x658
* SIZE: 0xD6 (214 bytes), 10 basic blocks, 48 instructions
* CALLERS: 1 (_ModuleEntryPoint)
* CALLS: 4 (sub_730/IsHobListGuid, sub_590/DebugPrint, sub_618/DebugAssert)
*
* This function:
* 1. Check if gHobList is already cached -> return immediately
* 2. Initialize gHobList = NULL
* 3. Get SystemTable->NumberOfTableEntries (offset 104 = 0x68 in ST)
* and SystemTable->ConfigurationTable (offset 112 = 0x70 in ST)
* 4. If NumberOfTableEntries > 0:
* - Iterate through ConfigurationTable entries (each 24 bytes)
* - Call IsHobListGuid() to compare each entry's VendorGuid
* against EFI_HOB_LIST_GUID
* - On match: set gHobList = ConfigTable[Index].VendorTable
* (field at offset 16 within a 24-byte config entry)
* 5. If not found:
* - Print "ASSERT_EFI_ERROR (Status = %r)" via DebugPrint
* - Call DebugAssert twice (HobLib.c:54 and :55)
* simulating the DxeHobLib ASSERT behavior
* 6. If gHobList is NULL after search + assertion, assert again
* 7. Return gHobList (may still be NULL if debug protocol fails)
*
* The HOB entry format:
* EFI_HOB_HEADER:
* +0x00: UINT16 HobType
* +0x02: UINT16 HobLength
* EFI_HOB_GUID_EXTENSION:
* +0x04: UINT32 Reserved
* +0x08: EFI_GUID Name (16 bytes)
* Total: 24 bytes per configuration table entry (SystemTable format)
*
* The SystemTable->ConfigurationTable entries are 24 bytes each:
* +0x00: EFI_GUID VendorGuid (16 bytes)
* +0x10: VOID *VendorTable (8 bytes)
*
* @param[in] ImageHandle The driver image handle (passed through from
* entry point, may be unused).
*
* @return Pointer to the HOB list, or NULL if not found.
*
* @note This function caches its result in gHobList (at 0xDC8).
* On first call, it scans the configuration table. On subsequent
* calls, it returns the cached result immediately.
*
* @note If the HOB list is not found, this function will trigger
* assertion calls via DebugAssert even if debug is disabled.
* The ASSERT_EFI_ERROR print uses EFI_UNSUPPORTED
* (0x800000000000000E) as the error status.
*
* @note The configuration table entry count comes from
* gST->NumberOfTableEntries (offset 104 from SystemTable).
* The table pointer is at gST->ConfigurationTable (offset 112).
* Each entry is 24 bytes: 16 bytes GUID + 8 bytes pointer.
*/
VOID *
EFIAPI
GetHobList (
IN EFI_HANDLE ImageHandle
)
{
UINTN Index;
UINTN ConfigTableCount;
EFI_CONFIGURATION_TABLE *ConfigTable;
// Check cache first
if (gHobList != NULL) {
return gHobList;
}
// Initialize to NULL
gHobList = NULL;
ConfigTableCount = gST->NumberOfTableEntries;
if (ConfigTableCount > 0) {
ConfigTable = gST->ConfigurationTable;
// Linear scan through configuration table entries
for (Index = 0; Index < ConfigTableCount; Index++) {
if (IsHobListGuid (ImageHandle, ConfigTable[Index].VendorGuid)) {
// Found the HOB list - extract the table pointer
gHobList = ConfigTable[Index].VendorTable;
break;
}
}
}
// If HOB list was not found, trigger assertion (matches DxeHobLib.c behavior)
if (gHobList == NULL) {
DebugPrint (DEBUG_INFO, L"\nASSERT_EFI_ERROR (Status = %r)\n", EFI_UNSUPPORTED);
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 gHobList;
}
// ============================================================================
// Function: IsHobListGuid (sub_730) - [0x730-0x79D, 110 bytes, complex]
// ============================================================================
/**
* Compares a GUID against the EFI_HOB_LIST_GUID by comparing its first
* 8 bytes and second 8 bytes independently.
*
* ADDRESS: 0x730
* SIZE: 0x6E (110 bytes), 5 basic blocks, 30 instructions
* CALLERS: 1 (sub_658/GetHobList)
* CALLS: 4 (sub_7A0/ReadUnaligned64 x4)
*
* This function reads the EFI_HOB_LIST_GUID from the binary's static
* data (at 0xC10) and compares it against a target GUID by splitting
* each GUID into two 64-bit halves.
*
* EFI_HOB_LIST_GUID = {7739F24C-93D7-11D4-9A3A-0090273FC14D}
* First 8 bytes (LE): 0xD411D7934CF23977
* (bytes: 4C F2 39 77 D7 93 D4 11)
* Second 8 bytes (LE): 0x4DC13F2790003A9A
*
* The GUID in memory at 0xC10 is:
* Data1_LE: 0x7739F24C
* Data2_LE: 0xD793
* Data3_LE: 0x11D4
* Data4[]: {0x9A, 0x3A, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D}
*
* The comparison function at sub_7A0 (ReadUnaligned64) reads the
* addressed bytes safely from potentially unaligned addresses,
* which is important because configuration table entries may
* not be 8-byte aligned.
*
* @param[in] ImageHandle Unused parameter (passed through from GetHobList).
* @param[in] GuidPtr Pointer to the GUID to compare against
* EFI_HOB_LIST_GUID.
*
* @retval TRUE The GUID matches EFI_HOB_LIST_GUID.
* @retval FALSE The GUID does not match.
*
* @note The comparison uses ReadUnaligned64() for both halves
* of the GUID to handle potential misaligned accesses.
*
* @note The reference GUID is read directly from the binary's
* .data section at addresses unk_C10 and unk_C18, which are
* the first 8 bytes and second 8 bytes of the EFI_HOB_LIST_GUID
* stored at 0xC10.
*/
BOOLEAN
EFIAPI
IsHobListGuid (
IN EFI_HANDLE ImageHandle,
IN EFI_GUID *GuidPtr
)
{
UINT64 GuidFirstHalf;
UINT64 GuidSecondHalf;
UINT64 RefFirstHalf;
UINT64 RefSecondHalf;
// Read the target GUID as two 64-bit values
GuidFirstHalf = ReadUnaligned64 (GuidPtr);
GuidSecondHalf = ReadUnaligned64 ((UINT8 *)GuidPtr + 8);
// Read the reference GUID (EFI_HOB_LIST_GUID) as two 64-bit values
// The reference is stored at gEfiHobListGuid (0xC10)
RefFirstHalf = ReadUnaligned64 (&gEfiHobListGuid);
RefSecondHalf = ReadUnaligned64 ((UINT8 *)&gEfiHobListGuid + 8);
// Compare both halves independently
return (RefFirstHalf == GuidFirstHalf)
&& (RefSecondHalf == GuidSecondHalf);
}
// ============================================================================
// Function: ReadUnaligned64 (sub_7A0) - [0x7A0-0x7CE, 47 bytes, wrapper]
// ============================================================================
/**
* Reads an unaligned 64-bit value from memory.
*
* ADDRESS: 0x7A0
* SIZE: 0x2F (47 bytes), 3 basic blocks, 13 instructions
* CALLERS: 4 (sub_730/IsHobListGuid x4)
* CALLS: 1 (DebugAssert if Buffer is NULL)
*
* Wraps the BaseLib ReadUnaligned64() function with a NULL pointer check.
* If Buffer is NULL, triggers a debug assertion via DebugAssert.
*
* @param[in] Buffer Pointer to the memory to read. Must not be NULL.
*
* @return The 64-bit value read from the given memory address.
*
* @note This function does NOT perform an actual unaligned load
* on modern x64 (which handles misaligned access natively).
* It simply dereferences the pointer as UINT64*. The "unaligned"
* name reflects the source API (BaseLib ReadUnaligned64) which
* is designed for architectures that do not support unaligned
* access.
*
* @note ASSERT file/line reference: e:\hs\MdePkg\Library\BaseLib\
* Unaligned.c, line 192: "Buffer != ((void *) 0)"
*/
UINT64
EFIAPI
ReadUnaligned64 (
IN CONST VOID *Buffer
)
{
if (Buffer == NULL) {
DebugAssert (
"e:\\hs\\MdePkg\\Library\\BaseLib\\Unaligned.c",
192,
"Buffer != ((void *) 0)"
);
}
// Read 8 bytes from the given address
return *(CONST UINT64 *)Buffer;
}
// ============================================================================
// Function: AllocatePool (inline wrapper, part of sub_510)
// ============================================================================
/**
* Allocates boot services pool memory.
*
* @param[in] Size Allocation size in bytes.
*
* @return Pointer to allocated memory, or NULL on failure.
*/
VOID *
EFIAPI
AllocatePool (
IN UINTN Size
)
{
return gBS->AllocatePool (EfiBootServicesData, Size);
}
// ============================================================================
// Function: FreePool (inline wrapper, part of sub_510)
// ============================================================================
/**
* Frees pool memory previously allocated by AllocatePool().
*
* @param[in] Buffer Pointer to memory to free.
*/
VOID
EFIAPI
FreePool (
IN VOID *Buffer
)
{
gBS->FreePool (Buffer);
}
// ============================================================================
// Function: RegisterIioConfig (sub_43C) - [0x43C-0x509, 207 bytes, complex]
// ============================================================================
/**
* Registers all IIO configuration protocol instances for this platform.
*
* ADDRESS: 0x43C
* SIZE: 0xCF (207 bytes), 6 basic blocks, 48 instructions
* CALLERS: 1 (_ModuleEntryPoint)
* CALLS: 2 (sub_590/DebugPrint, gBS->LocateProtocol + protocol->RegisterConfig x4)
*
* This function implements the core purpose of this driver:
* 1. Print a debug banner identifying the module:
* "UBA:IioCfgUpdate-TypeLightningRidgeEXECB2"
* 2. Locate the UBA board-type protocol via gBS->LocateProtocol()
* with protocol GUID at address 0xBF0 (UBA_BOARD_TYPE_PROTOCOL_GUID)
* 3. If LocateProtocol fails, return the EFI_ERROR status
* 4. For each of the 4 CPU sockets (IIO instances):
* - Call the board-type protocol's registration function
* at interface offset 0x10 (RegisterConfig)
* - Pass the socket-specific IIO config GUID
* - Pass the same UBA_IIO_CFG_UPDATE_PROTOCOL structure (48 bytes)
* which contains:
* * Signature: "PIIO"
* * Version: 1
* * TotalSize: 0x0D60
* * ConfigTableOffset: 0x3C
* * CallbackFunction: ReturnNotFound (pointer)
* * ResourceTableOffset: 0x0C60
* * ResourceTableSize: 0xFC
* 5. If any registration call fails, stop and return the error
* 6. Return EFI_SUCCESS if all 4 registrations succeed
*
* The registration function at protocol offset 0x10 has signature:
* EFI_STATUS (EFIAPI *)(VOID *This, EFI_GUID *ProtocolGuid,
* VOID *ConfigData, UINT64 ConfigDataSize)
*
* @return EFI_SUCCESS All IIO configurations were registered successfully.
* @return LocateProtocol error if UBA board-type protocol is not available.
* @return Registration error if one or more RegisterConfig calls fail.
*
* @note The protocol GUIDs for each socket are at addresses:
* - Socket 0: 0xBC0 = {6FE6C559-4F35-4111-98E1-332A251512F3}
* - Socket 1: 0xBE0 = {0F722F2A-650F-448A-ABB7-04EECD75BB30}
* - Socket 2: 0xC00 = {EBD11A00-8C5C-4F71-BB9E-5394032B01F4}
* - Socket 3: 0xC20 = {123BD082-3201-465C-B139-0CB8C77208F8}
*
* @note The board-type protocol interface is located via gBS->LocateProtocol
* at BootServices function offset 0x140 (320 = LocateProtocol).
* The interface has the registration function at offset 0x10 (16).
*
* @note Each registration passes ConfigDataSize = 48 (0x30) bytes,
* which is the size of UBA_IIO_CFG_UPDATE_PROTOCOL.
*/
EFI_STATUS
EFIAPI
RegisterIioConfig (
VOID
)
{
EFI_STATUS Status;
UBA_BOARD_TYPE_PROTOCOL *Protocol;
UINTN Index;
// Print debug banner identifying this platform module
DebugPrint (DEBUG_INFO, "UBA:IioCfgUpdate-TypeLightningRidgeEXECB2\n");
// Locate the UBA board-type protocol via gBS->LocateProtocol
// The protocol GUID is UBA_BOARD_TYPE_PROTOCOL_GUID at 0xBF0
Status = gBS->LocateProtocol (
&gUbaBoardTypeProtocolGuid,
NULL,
(VOID **)&Protocol
);
if (EFI_ERROR (Status)) {
return Status;
}
// Register IIO configuration protocol for each CPU socket
for (Index = 0; Index < IIO_CFG_UPDATE_PROTOCOL_COUNT; Index++) {
Status = Protocol->RegisterConfig (
Protocol,
&gIioCfgUpdateProtocolGuid[Index],
(UBA_IIO_CFG_UPDATE_PROTOCOL *)&mIioCfgProtocol,
sizeof (mIioCfgProtocol) // 48 bytes
);
if (EFI_ERROR (Status)) {
break;
}
}
return Status;
}
// ============================================================================
// Function: _ModuleEntryPoint (sub_390) - [0x390-0x438, 169 bytes, complex]
// ============================================================================
/**
* Module entry point for IioCfgUpdateDxeLightningRidgeEXECB2.
*
* ADDRESS: 0x390
* SIZE: 0xA9 (169 bytes), 9 basic blocks, 38 instructions
* CALLERS: 0 (this is the entry point, called by DXE dispatcher)
* CALLS: 4 (sub_618/DebugAssert x4, sub_658/GetHobList, sub_43C/RegisterIioConfig)
*
* Called by the DXE Dispatcher when this driver is loaded. Performs:
* 1. Caches UEFI global variables from the provided SystemTable:
* - gImageHandle = ImageHandle
* - gST = SystemTable
* - gBS = SystemTable->BootServices
* - gRT = SystemTable->RuntimeServices
* 2. Validates each pointer is non-NULL via DebugAssert
* 3. Locates the HOB list from the system configuration table
* (required by GetDebugProtocol which checks HOB allocation)
* 4. Calls RegisterIioConfig() to register IIO configuration
* protocols for all 4 CPU sockets
*
* The ASSERT checks match the standard UEFI library initialization
* pattern from UefiBootServicesTableLib and UefiRuntimeServicesTableLib:
* - gImageHandle != NULL (UefiBootServicesTableLib.c:51)
* - gST != NULL (UefiBootServicesTableLib.c:57)
* - gBS != NULL (UefiBootServicesTableLib.c:63)
* - gRT != NULL (UefiRuntimeServicesTableLib.c:47)
*
* @param[in] ImageHandle The firmware-allocated handle for this driver image.
* @param[in] SystemTable A pointer to the EFI System Table.
*
* @return EFI_STATUS from RegisterIioConfig() which returns:
* - EFI_SUCCESS if IIO configurations were registered.
* - Error code if LocateProtocol or registration fails.
*
* @note The HOB list is located via GetHobList() even if the caller
* doesn't directly need it, because GetDebugProtocol() (called by
* DebugPrint in the debug banner) checks the HOB allocation size.
*
* @note This entry point follows the standard UEFI DXE driver pattern:
* save handles -> validate -> do work -> return status.
*/
EFI_STATUS
EFIAPI
_ModuleEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
// ==================================================================
// Step 1: Cache UEFI global variables with NULL pointer validation
// ==================================================================
// gImageHandle: handle for this driver image
gImageHandle = ImageHandle;
if (gImageHandle == NULL) {
DebugAssert (
"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
51,
"gImageHandle != ((void *) 0)"
);
}
// gST: pointer to the UEFI System Table
gST = SystemTable;
if (gST == NULL) {
DebugAssert (
"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
57,
"gST != ((void *) 0)"
);
}
// gBS: pointer to the UEFI Boot Services Table
gBS = SystemTable->BootServices;
if (gBS == NULL) {
DebugAssert (
"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
63,
"gBS != ((void *) 0)"
);
}
// gRT: pointer to the UEFI Runtime Services Table
gRT = SystemTable->RuntimeServices;
if (gRT == NULL) {
DebugAssert (
"e:\\hs\\MdePkg\\Library\\UefiRuntimeServicesTableLib\\UefiRuntimeServicesTableLib.c",
47,
"gRT != ((void *) 0)"
);
}
// ==================================================================
// Step 2: Locate the HOB list (needed by GetDebugProtocol/GetHobList)
// ==================================================================
GetHobList (ImageHandle);
// ==================================================================
// Step 3: Register IIO configuration protocols for all sockets
// ==================================================================
Status = RegisterIioConfig ();
return Status;
}