/** @file
SlotDataUpdateDxeLightningRidgeEXECB3.c - Slot Data Update DXE Driver
This driver implements the SlotDataUpdate functionality for the Lightning
Ridge EX EC B3 platform using the UBA (Universal BIOS Agent) framework.
It reads Platform Slot Table (PSLT) configuration from HOBs stored in
the UEFI Configuration Table and the UBA HOB Database Protocol.
The driver registers platform slot information that defines PCIe slot
mapping for the motherboard: which physical slots map to which PCIe
root ports, their hotplug capabilities, and other slot-specific attributes.
File locations (from debug strings):
- MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.c
- MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.c
- MdePkg/Library/DxeHobLib/HobLib.c
- MdePkg/Library/BaseLib/Unaligned.c
UEFI Phase: DXE (Driver Execution Environment)
Protocol Dependencies:
- UBA HOB Database Protocol (custom)
- EFI_HOB_LIST_GUID in Configuration Table
Copyright (c) 2025, Lenovo. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "SlotDataUpdateDxeLightningRidgeEXECB3.h"
// ============================================================================
// Global Variables (EFI System Table globals, matched to BootServicesTableLib)
// ===========================================================================/
///
/// The EFI image handle for this driver.
/// Stored from ImageHandle parameter of ModuleEntryPoint.
/// @{ 0x0000 in .data, initialized by entry point
EFI_HANDLE gImageHandle = NULL;
/// @}
///
/// The EFI System Table pointer for this driver.
/// Stored from SystemTable parameter of ModuleEntryPoint.
/// @{ 0x0008 in .data (relative), initialized by entry point
EFI_SYSTEM_TABLE *gSystemTable = NULL;
/// @}
///
/// The EFI Boot Services Table pointer, cached from SystemTable->BootServices.
/// @{ 0x0010 in .data (relative), initialized by entry point
EFI_BOOT_SERVICES *gBootServices = NULL;
/// @}
///
/// The EFI Runtime Services Table pointer, cached from SystemTable->RuntimeServices.
/// @{ 0x0018 in .data (relative), initialized by entry point
EFI_RUNTIME_SERVICES *gRuntimeServices = NULL;
/// @}
///
/// Cached HOB list pointer from the EFI Configuration Table.
/// Found by scanning ConfigurationTable for EFI_HOB_LIST_GUID.
/// @{ At address 0xC28 in .data, zero-initialized
VOID *gHobList = NULL;
/// @}
///
/// Cached UBA HOB Database Protocol interface pointer.
/// Retrieved via gBS->LocateProtocol() on first access.
/// @{ At address 0xC30 in .data, zero-initialized
VOID *gUbaHobProtocol = NULL;
/// @}
// ============================================================================
// Global Data: PSLT Structure Templates and HOB Output Buffers
// ===========================================================================/
///
/// EFI_HOB_LIST_GUID value stored for Configuration Table comparison.
/// {7739F24C-93D7-11D4-9A3A-0090273FC14D}
/// Used by GetHobListFromConfigTable() to find the HOB list in the
/// System Table's ConfigurationTable array.
/// @{ At address 0xBA0 in .data (in .rdata section, read-only)
EFI_GUID gEfiHobListGuid = EFI_HOB_LIST_GUID;
/// @}
///
/// GUID for the UBA HOB Database Protocol.
/// Used with gBS->LocateProtocol() to find the HOB database interface.
/// {E03E0D46-5263-4845-B0A4-58D57B3177E2}
/// @{ At address 0xB70 in .data (in .rdata section, read-only)
EFI_GUID gUbaHobDatabaseProtocolGuid = UBA_HOB_DATABASE_PROTOCOL_GUID;
/// @}
///
/// GUID for the PSLT (Platform Slot Table) HOB entry (40-byte output).
/// {226763AE-972C-4E3C-80D1-73B25E8CBBA3}
/// @{ At address 0xB80 in .data (in .rdata section, read-only)
EFI_GUID gLightningRidgeExecb3PsltHobGuid = LIGHTNING_RIDGE_EXECB3_PSLT_HOB_GUID;
/// @}
///
/// GUID for the variant HOB entry (32-byte output).
/// {B93613E1-48F0-4B32-B3A8-4FEDFC7C1365}
/// @{ At address 0xB90 in .data (in .rdata section, read-only)
EFI_GUID gLightningRidgeExecb3VariantHobGuid = LIGHTNING_RIDGE_EXECB3_VARIANT_HOB_GUID;
/// @}
///
/// Output buffer for the variant HOB query (32 bytes).
/// Receives slot table data from the first HOB GUID query.
/// Contains PSLT header data with platform-specific slot configuration.
/// @{ At address 0xBC0 in .data (actually .rdata in original PE layout)
PLATFORM_SLOT_TABLE gSlotTableData = {
.Signature = PSLT_SIGNATURE, ///< "PSLT" at offset +0x00
.Version = PSLT_VERSION, ///< Version 1 at offset +0x04
.Reserved1 = 0, ///< At offset +0x08
.Reserved2 = 0x4B0, ///< At offset +0x0C (platform function table base?)
.Reserved3 = 1, ///< Count field at offset +0x10
.Count = 1 ///< Number of slot entries at offset +0x14
};
/// @}
///
/// Output buffer for the extended PSLT HOB query (40 bytes).
/// Contains PSLT header plus additional slot data.
/// @{ At address 0xBE0 in .data (actually .rdata in original PE layout)
UINT8 gSlotTableDataEx[40] = {
/// Same PSLT header as gSlotTableData (32 bytes)
[0x00 ... 0x1F] = 0x00,
/// Extended data at offset +0x20
[0x20] = 0xB4, [0x21] = 0x04, [0x22 ... 0x27] = 0x00 ///< Additional field
};
/// @}
// ============================================================================
// Function Implementations
// ============================================================================
/**
Returns platform type code for this driver's target.
Lightning Ridge EX EC B3 returns platform type 2.
This function is referenced as a callback at address 0x4B0 in the
PSLT data structure's function table.
@return Platform type code (2).
**/
UINT8
EFIAPI
GetPlatformType (
VOID
)
{
//
// Platform type 2 = Lightning Ridge EX EC B3
//
return 2;
}
/**
Passthrough function for platform callback.
Returns the value passed in a2 unchanged. This is used when no
transformation is needed for the platform callback chain.
@param[in] Context Callback context (unused)
@param[in] Value Input value to pass through
@return Value unchanged.
**/
UINT8
EFIAPI
PlatformPassthrough (
IN VOID *Context,
IN UINT8 Value
)
{
//
// Simple identity function - return the input value
//
return Value;
}
/**
Locates and caches the UBA HOB Database Protocol.
This function implements a TPL (Task Priority Level) guard to ensure
protocol location is only attempted when the current TPL is at or
below TPL_NOTIFY (16). If the TPL is higher, LocateProtocol could
potentially block or deadlock.
Protocol is only located once and cached in gUbaHobProtocol for
subsequent calls.
On the first call:
1. Raises TPL to TPL_HIGH_LEVEL (31) via gBS->RaiseTPL()
2. If OldTpl <= TPL_NOTIFY (16):
a. Calls gBS->LocateProtocol() with gUbaHobDatabaseProtocolGuid
b. Caches result in gUbaHobProtocol
3. Restores TPL via gBS->RestoreTPL(OldTpl)
@return Pointer to the UBA_HOB_DATABASE_PROTOCOL interface,
or NULL if not found / TPL too high.
@note The first 8 bytes of the protocol interface are reserved.
Debug print is at offset +0x08.
GetNextGuidHob is at offset +0x10.
**/
UBA_HOB_DATABASE_PROTOCOL *
EFIAPI
GetUbaHobDatabaseProtocol (
VOID
)
{
UBA_HOB_DATABASE_PROTOCOL *Protocol;
EFI_TPL OldTpl;
//
// Return cached protocol if already found
//
if (gUbaHobProtocol != NULL) {
return (UBA_HOB_DATABASE_PROTOCOL *)gUbaHobProtocol;
}
//
// TPL guard: only locate protocol if current TPL <= TPL_NOTIFY (16)
// Raise to TPL_HIGH_LEVEL (31) to check, then restore
//
OldTpl = gBootServices->RaiseTPL (TPL_HIGH_LEVEL);
gBootServices->RestoreTPL (OldTpl);
if (OldTpl <= TPL_NOTIFY) {
//
// Locate the UBA HOB Database Protocol
//
gBootServices->LocateProtocol (
&gUbaHobDatabaseProtocolGuid,
NULL,
&gUbaHobProtocol
);
}
return (UBA_HOB_DATABASE_PROTOCOL *)gUbaHobProtocol;
}
/**
Uses the UBA HOB Database Protocol to log a debug message.
If the UBA protocol has been located, calls the debug print function
at offset +0x08 in the protocol interface. Otherwise, silently does
nothing.
@param[in] DebugLevel Debug message severity level (e.g., UBA_DEBUG_LEVEL_INFO).
@param[in] Format Format string.
@param[in] ... Variable arguments.
@return Status code from the debug print function (typically 0 = success),
or 4 (default) if protocol is not available.
**/
UINT8
EFIAPI
UbaDebugPrint (
IN UINTN DebugLevel,
IN CONST CHAR8 *Format,
...
)
{
UBA_HOB_DATABASE_PROTOCOL *Protocol;
UINTN Result;
VA_LIST VaList;
UINT64 DebugFlags;
Protocol = GetUbaHobDatabaseProtocol ();
if (Protocol == NULL) {
return 4;
}
//
// The debug print function at offset +0x08 in the protocol has signature:
// UINTN (*)(UINTN DebugLevel, CONST CHAR8 *Format, VA_LIST Args)
//
// It uses a debug flags mask: if (DebugFlags & DebugLevel) is non-zero,
// the message is printed. Otherwise it is supressed.
//
DebugFlags = 0x80000004; ///< Default mask: allow INFO and ERROR levels
if ((DebugFlags & DebugLevel) != 0) {
VA_START (VaList, Format);
//
// Call protocol debug function (UINT64 function pointer at offset +0x08
// to match x64 calling convention padding)
//
Result = ((UINT64 (*)(VOID *, UINTN, CONST CHAR8 *, VA_LIST))
(((UINT64 *)Protocol)[1])) (
Protocol,
DebugLevel,
Format,
VaList
);
VA_END (VaList);
return (UINT8)Result;
}
return 4;
}
/**
Assert and deadloop handler.
Prints assert information containing the file name, line number, and
condition text, then enters an infinite loop (deadloop). Called by the
ASSERT() and ASSERT_EFI_ERROR() macros.
@param[in] FileName Source file name string
@param[in] LineNumber Line number in source file
@param[in] AssertText Assert condition text
**/
VOID
EFIAPI
UbaAssert (
IN CHAR8 *FileName,
IN UINTN LineNumber,
IN CHAR8 *AssertText
)
{
//
// Log assert with UBA debug protocol
//
UbaDebugPrint (
UBA_DEBUG_LEVEL_STATUS,
"\nASSERT_EFI_ERROR (Status = %r)\n",
0x800000000000000EuLL ///< EFI_ABORTED as format argument
);
//
// Deadloop - CPU will spin here forever
// This matches the original decompiled behavior
//
while (TRUE) {
CpuDeadLoop ();
}
}
/**
Scans the EFI System Table ConfigurationTable for an entry whose
GUID matches gEfiHobListGuid, and caches the HOB list pointer.
The EFI System Table contains a ConfigurationTable array where each
entry is 24 bytes: a 16-byte GUID followed by an 8-byte interface
pointer. This function walks that array looking for EFI_HOB_LIST_GUID.
Steps:
1. If gHobList is already non-NULL, return it immediately (cache hit)
2. Clear gHobList to NULL
3. If SystemTable->NumberOfTableEntries is 0, ASSERT and deadloop
4. Walk ConfigurationTable entries:
- Each entry is 24 bytes: GUID(16) + Interface(8)
- Compare GUID by splitting into two 8-byte halves
- The two template values come from gEfiHobListGuid:
[0:7] = first 8 bytes of the GUID
[8:15] = second 8 bytes of the GUID
- If match found, save Interface[16] pointer into gHobList
5. If gHobList is still NULL after walk, ASSERT and deadloop
This is functionally equivalent to the DXE-phase GetHobList()
from DxeHobLib but implemented as an inline scan of the
ConfigurationTable rather than using a library call.
@return Pointer to the cached HOB list (gHobList)
**/
VOID *
EFIAPI
GetHobListFromConfigTable (
VOID
)
{
UINTN Index;
UINTN TableEntryCount;
VOID *ConfigTable;
//
// Return cached HOB list if already found
//
if (gHobList != NULL) {
return gHobList;
}
//
// Get Configuration Table info from SystemTable
// SystemTable + 0x68 = NumberOfTableEntries
// SystemTable + 0x70 = ConfigurationTable pointer
//
gHobList = NULL;
TableEntryCount = gSystemTable->NumberOfTableEntries;
ConfigTable = gSystemTable->ConfigurationTable;
if (TableEntryCount == 0) {
//
// No configuration table entries - this is an error
// Log assert and enter deadloop
//
UbaDebugPrint (
UBA_DEBUG_LEVEL_STATUS,
"\nASSERT_EFI_ERROR (Status = %r)\n",
0x800000000000000EuLL ///< EFI_ABORTED
);
UbaAssert (
"e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
54,
"!EFI_ERROR (Status)"
);
return gHobList;
}
//
// Walk the Configuration Table entries looking for EFI_HOB_LIST_GUID
// Each entry is 24 bytes: GUID(16) + Interface pointer(8)
//
for (Index = 0; Index < TableEntryCount; Index++) {
//
// Calculate current entry address (24 bytes per entry)
//
VOID *Entry = (UINT8 *)ConfigTable + (Index * 24);
//
// Check if this entry's GUID matches EFI_HOB_LIST_GUID
// Compare by splitting both GUIDs into two 8-byte halves
//
if (CompareGuidQwords (Entry, &gEfiHobListGuid, (UINT8 *)&gEfiHobListGuid + 8)) {
//
// Found the HOB list - cache the interface pointer at offset +0x10
//
gHobList = *(VOID **)((UINT8 *)Entry + 16);
break;
}
}
//
// If HOB list was not found, ASSERT
//
if (gHobList == NULL) {
UbaAssert (
"e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
55,
"mHobList != ((void *) 0)"
);
}
return gHobList;
}
/**
Compares two GUIDs by matching their first and second 8-byte halves.
Since the ConfigurationTable entries store GUIDs as 16-byte contiguous
blobs, this function efficiently compares GUIDs by splitting into
two QWORD comparisons instead of byte-by-byte or using full GUID
comparison protocol.
@param[in] EntryAddr Address of the ConfigurationTable entry (or any
structure with 16 bytes of GUID data at start)
@param[in] Guid1 Pointer to 8 bytes representing the first half
(bytes 0-7) of the target GUID
@param[in] Guid2 Pointer to 8 bytes representing the second half
(bytes 8-15) of the target GUID
@retval TRUE GUID at EntryAddr matches {Guid1, Guid2}
@retval FALSE GUID does not match
**/
BOOLEAN
EFIAPI
CompareGuidQwords (
IN VOID *EntryAddr,
IN VOID *Guid1,
IN VOID *Guid2
)
{
UINT64 EntryQword0;
UINT64 EntryQword1;
UINT64 TemplateQword0;
UINT64 TemplateQword1;
//
// Read unaligned QWORDs from both entries
//
EntryQword0 = ReadUnaligned64 (EntryAddr);
EntryQword1 = ReadUnaligned64 ((UINT8 *)EntryAddr + 8);
TemplateQword0 = ReadUnaligned64 (Guid1);
TemplateQword1 = ReadUnaligned64 (Guid2);
//
// Both halves must match
//
return (BOOLEAN)(EntryQword0 == TemplateQword0 && EntryQword1 == TemplateQword1);
}
/**
Reads an unaligned 64-bit value from memory with NULL pointer check.
This is functionally equivalent to ReadUnaligned64() from BaseLib.
Asserts if Buffer is NULL.
@param[in] Buffer Pointer to the 8-byte value to read (must not be NULL)
@return 64-bit value at the given address, read unaligned.
**/
UINT64
EFIAPI
ReadUnaligned64 (
IN CONST VOID *Buffer
)
{
//
// NULL pointer check (asserts and deadloops if NULL)
//
if (Buffer == NULL) {
UbaAssert (
"e:\\hs\\MdePkg\\Library\\BaseLib\\Unaligned.c",
192,
"Buffer != ((void *) 0)"
);
}
//
// Read 8 bytes unaligned
//
return *(volatile UINT64 *)Buffer;
}
/**
Main entry point for the SlotDataUpdateDxeLightningRidgeEXECB3 driver.
This is the core initialization routine for the SlotDataUpdate driver.
It performs the following operations in sequence:
Phase 1 - Initialize EFI Service Pointers:
1a. Save ImageHandle to gImageHandle global (NULL check)
1b. Save SystemTable to gSystemTable global (NULL check)
1c. Cache BootServices from SystemTable (NULL check)
1d. Cache RuntimeServices from SystemTable (NULL check)
Phase 2 - Initialize HOB List Access:
2a. Call GetHobListFromConfigTable() to locate and cache the HOB list
2b. This scans SystemTable->ConfigurationTable for EFI_HOB_LIST_GUID
Phase 3 - Platform Identification:
3a. Debug print "UBA:SlotDataUpdate-TypeLightningRidgeEXECB3" to
identify this module's platform target in the UBA framework
3b. This distinguishes EC B3 from other board revisions (EC B1, B2, B4, etc.)
Phase 4 - Query UBA HOB Protocol for Slot Data:
4a. Locate the UBA HOB Database Protocol via gBS->LocateProtocol()
4b. Call protocol.GetNextGuidHob() with:
- gLightningRidgeExecb3VariantHobGuid => output to gSlotTableData (32 bytes)
- gLightningRidgeExecb3PsltHobGuid => output to gSlotTableDataEx (40 bytes)
@param[in] ImageHandle The firmware allocated handle for the EFI image.
@param[in] SystemTable A pointer to the EFI System Table.
@return EFI_SUCCESS The driver initialized successfully.
@return EFI_INVALID_PARAMETER One of the HOB database queries failed.
@return EFI_NOT_FOUND UBA HOB Database Protocol not found.
**/
EFI_STATUS
EFIAPI
SlotDataUpdateEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
UBA_HOB_DATABASE_PROTOCOL *UbaHobProtocol;
//
// PHASE 1: Initialize EFI Service Pointers
//
//
// Save ImageHandle - required for UEFI driver registration
// NULL check via ASSERT (from UefiBootServicesTableLib)
//
gImageHandle = ImageHandle;
if (gImageHandle == NULL) {
UbaAssert (
"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
51,
"gImageHandle != ((void *) 0)"
);
}
//
// Save SystemTable pointer
// NULL check via ASSERT
//
gSystemTable = SystemTable;
if (gSystemTable == NULL) {
UbaAssert (
"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
57,
"gST != ((void *) 0)"
);
}
//
// Cache Boot Services table pointer
// NULL check via ASSERT
//
gBootServices = SystemTable->BootServices;
if (gBootServices == NULL) {
UbaAssert (
"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
63,
"gBS != ((void *) 0)"
);
}
//
// Cache Runtime Services table pointer
// NULL check via ASSERT
//
gRuntimeServices = SystemTable->RuntimeServices;
if (gRuntimeServices == NULL) {
UbaAssert (
"e:\\hs\\MdePkg\\Library\\UefiRuntimeServicesTableLib\\UefiRuntimeServicesTableLib.c",
47,
"gRT != ((void *) 0)"
);
}
//
// PHASE 2: Initialize HOB List Access
// Locate the HOB list from the System Table Configuration Table
// This is needed for subsequent HOB queries
//
GetHobListFromConfigTable ();
//
// PHASE 3: Platform Identification
// Print platform identifier string for UBA framework debugging
//
UbaDebugPrint (
UBA_DEBUG_LEVEL_INFO,
"UBA:SlotDataUpdate-TypeLightningRidgeEXECB3\n"
);
//
// PHASE 4: Query UBA HOB Protocol for Slot Data
//
//
// Locate the UBA HOB Database Protocol
// This protocol provides access to platform-specific HOBs
//
Status = gBootServices->LocateProtocol (
&gUbaHobDatabaseProtocolGuid,
NULL,
(VOID **)&UbaHobProtocol
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Query variant HOB (32-byte output buffer)
// This retrieves platform slot table variant data that defines
// the PCIe slot configuration for this specific board revision.
//
Status = UbaHobProtocol->GetNextGuidHob (
UbaHobProtocol,
&gLightningRidgeExecb3VariantHobGuid,
&gSlotTableData,
32
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Query PSLT HOB (40-byte output buffer)
// This retrieves extended slot table data with additional
// platform-specific configuration fields.
//
Status = UbaHobProtocol->GetNextGuidHob (
UbaHobProtocol,
&gLightningRidgeExecb3PsltHobGuid,
&gSlotTableDataEx,
40
);
return Status;
}